EDA
Single Features
All of the features in this data set are typed as numerical. In turn,
indicator variables are changed to factor. For visualization, “male” is
changed to “sex”. The indicator values for the categorical variables
were changed to descriptive words or phrases to enhance readability of
the graphs.
# Set up plotting area
par(mfrow = c(2, 2))
# Histogram for age
hist(fhs$age,
main = "Age",
xlab = "Age",
col = "steelblue",
border = "white")
# Histogram for cigsPerDay
hist(fhs$cigsPerDay,
main = "Cigarettes Per Day",
xlab = "Cigarettes Per Day",
col = "steelblue",
border = "white")
# Histogram for totChol
hist(fhs$totChol,
main = "Total Cholesterol",
xlab = "Total Cholesterol",
col = "steelblue",
border = "white")
# Histogram for sysBP
hist(fhs$sysBP,
main = "Systolic Blood Pressure",
xlab = "Systolic BP",
col = "steelblue",
border = "white")

# Reset plotting layout to default
par(mfrow = c(1, 1))
Figure A. Histograms of age, CigsPerDay, totChol (response), and
sysBP.
# Set up 2x2 plotting area
par(mfrow = c(2, 2))
# Histogram for diastolic blood pressure
hist(fhs$diaBP,
main = "Diastolic Blood Pressure",
xlab = "Diastolic BP",
col = "steelblue",
border = "white")
# Histogram for BMI
hist(fhs$BMI,
main = "Body Mass Index (BMI)",
xlab = "BMI",
col = "steelblue",
border = "white")
# Histogram for heart rate
hist(fhs$heartRate,
main = "Heart Rate",
xlab = "Beats Per Minute",
col = "steelblue",
border = "white")
# Histogram for glucose
hist(fhs$glucose,
main = "Glucose Level",
xlab = "Glucose",
col = "steelblue",
border = "white")

# Reset to default plotting layout
par(mfrow = c(1, 1))
Figure B. Histograms for diaBP, BMI, heartRate, and glucose.
# Set up 2x2 plotting layout
par(mfrow = c(2, 2))
# Bar plot for sex
barplot(table(fhs$sex),
main = "Sex Distribution",
col = "steelblue",
names.arg = c("Female", "Male"),
ylab = "Count")
# Bar plot for current smoker
barplot(table(fhs$currentSmoker),
main = "Current Smoker Status",
col = "steelblue",
names.arg = c("No", "Yes"),
ylab = "Count")
# Bar plot for BP meds
barplot(table(fhs$BPMeds),
main = "Blood Pressure Medication",
col = "steelblue",
names.arg = c("No", "Yes"),
ylab = "Count")

Figure C. Bar charts for sex, currentSmoker and BPMeds.
# Set 2x2 layout for bar charts
par(mfrow = c(2, 2))
# Bar chart for prevalentStroke
barplot(table(fhs$prevalentStroke),
main = "Prevalent Stroke",
xlab = "0 = No, 1 = Yes",
col = "steelblue")
# Bar chart for prevalentHyp
barplot(table(fhs$prevalentHyp),
main = "Prevalent Hypertension",
xlab = "0 = No, 1 = Yes",
col = "steelblue")
# Bar chart for diabetes
barplot(table(fhs$diabetes),
main = "Diabetes",
xlab = "0 = No, 1 = Yes",
col = "steelblue")
# Bar chart for TenYearCHD
barplot(table(fhs$TenYearCHD),
main = "Ten-Year CHD",
xlab = "0 = No, 1 = Yes",
col = "steelblue")

Figure D. Bar charts for prevalentStroke, prevalentHyp, diabetes, and
TenYearCHD (response).
MISSING VALUES
Imputation is a critical step in data preprocessing, especially when
working with datasets containing missing values. It involves estimating
and replacing missing data with plausible values based on the available
information.
For the missing cigsPerDay values, any instances that are not current
smokers (currentSmoker = 0) and have missing cigsPerDay will be imputed
with zero. All other instances with missing cigsPerDay will be imputed
with the mean.
# Step 1: Save the original cigsPerDay before imputation
cigs_before <- fhs$cigsPerDay
# Step 2: Impute using currentSmoker
mean_cigs_smokers <- mean(fhs$cigsPerDay[fhs$currentSmoker == 1 & !is.na(fhs$cigsPerDay)], na.rm = TRUE)
fhs$cigsPerDay[is.na(fhs$cigsPerDay) & fhs$currentSmoker == 0] <- 0
fhs$cigsPerDay[is.na(fhs$cigsPerDay) & fhs$currentSmoker == 1] <- mean_cigs_smokers
# Step 3: Create a combined data frame for plotting
cigs_df <- rbind(
data.frame(value = cigs_before, group = "Before"),
data.frame(value = fhs$cigsPerDay, group = "After")
)
# Step 4: Plot the overlay density curve
ggplot(cigs_df, aes(x = value, fill = group, color = group)) +
geom_density(alpha = 0.5) +
labs(title = "Density Plot of cigsPerDay Before and After Imputation",
x = "Cigarettes Per Day", y = "Density") +
scale_fill_manual(values = c("Before" = "lightblue", "After" = "salmon")) +
scale_color_manual(values = c("Before" = "lightblue", "After" = "salmon")) +
theme_minimal()

Figure E. Post imputation, cigsPerDay has zero missing values. The
before and after imputation distributions are very similar.
The distributions of CigsPerDay before and after imputation are very
similar, indicating that the imputation process effectively preserved
the original data characteristics.
Multiple imputation by chained equations (MICE) is used to impute
missing values for cigPerDay, BPMeds, totChol, BMI, heartRate, and
glucose. MICE is an iterative method that creates multiple imputed
datasets, improving the reliability of the imputation process by
incorporating uncertainty in the missing values. The imputed values are
expected to provide a more complete dataset for subsequent analysis,
such as modeling and prediction.
# Create a data frame for the features before imputation (original data)
fhs_missing_before <- fhs[, c("cigsPerDay", "totChol", "BMI", "heartRate", "glucose")]
# Perform MICE imputation while fully suppressing console output
invisible(capture.output({
mice_imputation <- mice(fhs_missing_before, method = 'pmm', m = 5, maxit = 50, seed = 500)
}))
# Complete the imputed data
fhs_imputed <- complete(mice_imputation, action = 1)
# Prepare data for plotting (before and after)
fhs_before <- data.frame(value = as.vector(as.matrix(fhs_missing_before)),
variable = rep(colnames(fhs_missing_before), each = nrow(fhs_missing_before)),
dataset = rep("Before", nrow(fhs_missing_before) * ncol(fhs_missing_before)))
fhs_after <- data.frame(value = as.vector(as.matrix(fhs_imputed)),
variable = rep(colnames(fhs_imputed), each = nrow(fhs_imputed)),
dataset = rep("After", nrow(fhs_imputed) * ncol(fhs_imputed)))
# Combine the data for before and after into one data frame
fhs_combined <- rbind(fhs_before, fhs_after)
# Create the density plot with ggplot
ggplot(fhs_combined, aes(x = value, fill = dataset, color = dataset)) +
geom_density(alpha = 0.5) +
facet_wrap(~variable, scales = "free") +
labs(title = "Density Plots: Before and After Imputation",
x = "Value", y = "Density") +
theme_minimal() +
scale_fill_manual(values = c("Before" = "lightblue", "After" = "pink")) +
scale_color_manual(values = c("Before" = "lightblue", "After" = "pink"))
Figure F. Before and after imputation density curves.
The distributions of the variables before and after imputation are
very similar, indicating that the imputation process effectively
preserved the original data characteristics. The use of multiple
imputation by chained equations (MICE) has ensured that the imputed
values closely follow the patterns and spread of the observed data,
maintaining the integrity of the variables for further analysis. In
turn, the imputed values is added to the final working dataset for
analysis.
# Overwrite original columns in fhs with imputed values
fhs$cigsPerDay <- fhs_imputed$cigsPerDay
fhs$totChol <- fhs_imputed$totChol
fhs$BMI <- fhs_imputed$BMI
fhs$heartRate <- fhs_imputed$heartRate
fhs$glucose <- fhs_imputed$glucose
Prior to modeling, the variable BPMeds (whether the participant was
on blood pressure medication) contains missing values. To address this,
a logistic regression model is fitted using relevant predictors (age,
sysBP, diaBP, cigsPerDay, and prevalentHyp) based on cases with observed
BPMeds values. Predicted probabilities from this model were then used to
impute missing values, with a threshold of 0.5 to classify individuals
as either taking or not taking blood pressure medication.
# Save original BPMeds with NAs before imputation
fhs$BPMeds_original <- fhs$BPMeds
# Keep rows where BPMeds is not missing
bpm_complete <- fhs[!is.na(fhs$BPMeds_original), ]
# Fit logistic regression model
bpm_model <- glm(BPMeds_original ~ age + sysBP + diaBP + cigsPerDay + prevalentHyp,
data = bpm_complete, family = binomial)
# Identify rows with missing BPMeds
bpm_missing <- fhs[is.na(fhs$BPMeds_original), ]
# Identify rows among bpm_missing that have complete predictors
predictable_rows <- complete.cases(bpm_missing[, c("age", "sysBP", "diaBP", "cigsPerDay", "prevalentHyp")])
# Predict only on rows we can predict
if (any(predictable_rows)) {
predicted_probs <- predict(bpm_model, newdata = bpm_missing[predictable_rows, ], type = "response")
# Get the original row numbers for the missing data
missing_row_indices <- which(is.na(fhs$BPMeds_original))
# Find indices that are predictable
predictable_indices <- missing_row_indices[predictable_rows]
# Impute predicted values into the correct rows
fhs$BPMeds[predictable_indices] <- ifelse(predicted_probs >= 0.5, 1, 0)
}
# Make sure fhs$BPMeds and fhs$BPMeds_original are there
# Create a long-format data frame
bpm_plot_data <- data.frame(
value = c(fhs$BPMeds_original, fhs$BPMeds),
status = rep(c("Before Imputation", "After Imputation"), each = nrow(fhs))
)
# Clean bar plot
ggplot(bpm_plot_data, aes(x = factor(value), fill = status)) +
geom_bar(position = "dodge") +
labs(title = "BPMeds Before and After Imputation",
x = "BPMeds (0 = No, 1 = Yes)",
y = "Count",
fill = "Status") +
scale_x_discrete(labels = c("0" = "No", "1" = "Yes")) +
theme_minimal(base_size = 14)

Figure G. Side by side bar chart of before and after logistic
imputation.
The distribution of BPMeds before and after logistic imputation is
very similar, indicating that the imputation process preserved the
underlying structure of the data without introducing substantial
bias.
# Remove BPMeds_original column after imputation and plotting
fhs$BPMeds_original <- NULL
PAIRWISE
RELATIONSHIPS
To explore the relationships between the most important numerical
predictors of TenYearCHD, pairwise scatterplots are generated for the
top five variables identified through Random Forest feature selection.
These include systolic blood pressure (sysBP), body mass index (BMI),
total cholesterol (totChol), glucose, and age. Analyzing these pairwise
relationships helps to visually understand how these features correlate
with each other and with the target variable, TenYearCHD.
# Select the top 5 most important numerical features
top_features <- fhs[, c("sysBP", "BMI", "totChol", "glucose", "age")]
# Compute the correlation matrix
cor_matrix <- cor(top_features)
# Print the correlation matrix with numbers
#print(cor_matrix)
# Create a clean correlation plot with numbers displayed inside the cells
corrplot(cor_matrix,
method = "color", # Color-based visualization
type = "full", # Full matrix display
col = colorRampPalette(c("red", "white", "blue"))(200), # Color gradient
tl.cex = 0.8, # Adjust label size
number.cex = 0.8, # Adjust correlation number size
addCoef.col = "black", # Add correlation coefficients in black
title = "Correlation Matrix for Top 5 Numerical Features",
mar = c(0,0,1,0)) # Margins around the plot

Figure H. Correlation matrix for continuous features.
The scatterplot matrix below displays pairwise relationships between
the five most important numerical features identified for predicting
coronary heart disease. These features include systolic blood pressure
(sysBP), body mass index (BMI), total cholesterol (totChol), glucose,
and age. The scatterplots provide a visual representation of how these
variables relate to one another, with each plot showcasing the
distribution of data points between two features. The inclusion of
bootstrapping reduces clutter by sampling 30% of the data, allowing for
clearer insights into potential patterns and associations.
# Load necessary libraries
library(ggplot2)
library(gridExtra)
# Select the top 5 most important numerical features
top_features <- fhs[, c("sysBP", "BMI", "totChol", "glucose", "age")]
# Bootstrap the data (reduce size to 30% to declutter scatterplots)
set.seed(123) # For reproducibility
top_features_sample <- top_features[sample(1:nrow(top_features), 0.3 * nrow(top_features)), ]
# Create the scatterplot matrix manually
pairs_list <- list()
# Loop over all pairs of features to create individual scatterplots
for(i in 1:(ncol(top_features_sample)-1)) {
for(j in (i+1):ncol(top_features_sample)) {
pairs_list[[paste(names(top_features_sample)[i], names(top_features_sample)[j], sep = "_")]] <-
ggplot(top_features_sample, aes_string(x = names(top_features_sample)[i], y = names(top_features_sample)[j])) +
geom_point(alpha = 0.3, size = 0.5) +
labs(title = paste(names(top_features_sample)[i], "vs", names(top_features_sample)[j])) +
theme_minimal() +
theme(
plot.title = element_text(size = 10, face = "bold", hjust = 0.5, vjust = 0),
axis.title = element_text(size = 8),
axis.text = element_text(size = 7)
)
}
}
# Arrange all scatterplots in a grid layout
grid.arrange(grobs = pairs_list, ncol = 5) # Arrange in 5 columns, adjust ncol if needed

Figure I. Scatterplots of the five most important numeric
features.
The pairwise scatterplots reveal several moderate linear
relationships among the top features. Specifically, systolic blood
pressure (sysBP) and body mass index (BMI) exhibit a moderate linear
relationship, as do sysBP and total cholesterol (totChol), BMI and
totChol, sysBP and age, and totChol and age. These relationships suggest
that as one variable increases, there tends to be a corresponding
increase in the other, indicating a moderate association between these
key health factors.
# Set up the plotting area to have 2 rows and 3 columns
par(mfrow = c(2, 3))
# List of categorical variables to compare with TenYearCHD
categorical_vars <- c("sex", "currentSmoker", "BPMeds", "prevalentStroke", "prevalentHyp", "diabetes")
# Loop through each categorical variable
for (var in categorical_vars) {
# Create mosaic plot for each variable compared with TenYearCHD
mosaicplot(table(fhs[[var]], fhs$TenYearCHD), main = paste("TenYearCHD vs", var),
color = c("lightblue", "pink"),
xlab = paste(var), ylab = "TenYearCHD",
las = 1)
# Add labels for 1 = Yes, 0 = No
text(x = 1, y = 1, labels = "1 = Yes", col = "black", pos = 4)
text(x = 1, y = 2, labels = "0 = No", col = "black", pos = 4)
}

Figure J. Mosaic plots of relationships of categorical features.
The mosaic plots reveal that there is a significant association
between TenYearCHD and all the categorical variables, except for
“currentSmoker.” Specifically, variables like “sex,” “BPMeds,”
“prevalentStroke,” “prevalentHyp,” and “diabetes” show clear patterns
where the distribution of TenYearCHD differs across their levels.
However, “currentSmoker” does not appear to exhibit a strong association
with the outcome, suggesting that smoking status may not be a strong
predictor of TenYearCHD in this dataset.
FEATURE ENGINEER
To enhance model performance and uncover meaningful patterns, feature
engineering was applied to the Framingham Heart Study dataset. This
process included creating new variables, such as clusters, interaction
terms, and clinically relevant risk indicators, to better capture
complex relationships with ten-year cardiovascular disease risk.
# Remove non-numeric columns (like factors and response variable)
fhs_numeric <- fhs[sapply(fhs, is.numeric)]
fhs_scaled <- scale(fhs_numeric) # Standardize the features
set.seed(123)
# Standardize numeric variables
fhs_scaled <- scale(fhs[, sapply(fhs, is.numeric)])
# Compute WSS for k = 1 to 10
wss <- sapply(1:10, function(k){
kmeans(fhs_scaled, centers = k, nstart = 10)$tot.withinss
})
# Plot Elbow Curve
plot(1:10, wss, type = "b", pch = 19, frame = FALSE,
xlab = "Number of clusters K",
ylab = "Total within-clusters sum of squares",
main = "Elbow Method for Determining Optimal K")

# Run k-means with chosen number of clusters (e.g., 3)
kmeans_result <- kmeans(fhs_scaled, centers = 3, nstart = 25)
# Add cluster to the dataset
fhs$Cluster <- as.factor(kmeans_result$cluster)
# Run PCA just for plotting (2D representation)
pca_result <- prcomp(fhs_scaled)
# Create data frame for plotting
plot_data <- data.frame(PC1 = pca_result$x[,1],
PC2 = pca_result$x[,2],
Cluster = fhs$Cluster)
# Plot the clusters
library(ggplot2)
ggplot(plot_data, aes(x = PC1, y = PC2, color = Cluster)) +
geom_point(size = 2, alpha = 0.8) +
labs(title = "K-Means Clustering (Visualized via PCA)",
x = "Principal Component 1",
y = "Principal Component 2") +
theme_minimal() +
scale_color_brewer(palette = "Set1")

Figure K. Visualization of clusters.
The cluster analysis revealed three well-separated and distinct
groups within the Framingham Heart dataset, suggesting meaningful
underlying patterns in the data. These clusters may represent different
cardiovascular risk profiles, which could help inform targeted
prevention or intervention strategies.
Hyperparameter
Tuning
To improve model performance and prevent overfitting, we performed
hyperparameter tuning on the CART regression model using 10-fold
cross-validation. Specifically, we tuned the complexity parameter (cp),
which controls the trade-off between tree size and predictive
accuracy.
# Set seed for reproducibility
set.seed(123)
# Build the initial regression tree with cross-validation
tree_model <- rpart(
totChol ~ .,
data = train_data,
method = "anova", # Regression tree
control = rpart.control(
minsplit = 10,
minbucket = 5,
cp = 0.01,
maxdepth = 5,
xval = 10
)
)
# View the complexity parameter table
pander(tree_model$cptable)
| 0.1619 |
0 |
1 |
1.001 |
0.04969 |
| 0.01281 |
1 |
0.8381 |
0.8389 |
0.04484 |
| 0.01185 |
2 |
0.8253 |
0.841 |
0.04547 |
| 0.01034 |
3 |
0.8134 |
0.8255 |
0.04547 |
| 0.01 |
4 |
0.8031 |
0.819 |
0.0453 |
# Plot the complexity parameter vs. error (xerror)
plotcp(tree_model)

# Choose the optimal cp based on xerror
min_xerror <- min(tree_model$cptable[, "xerror"])
best_cp_row <- which.min(tree_model$cptable[, "xerror"])
best_cp <- tree_model$cptable[best_cp_row, "CP"]
# Print the best cp value
cat("Best CP: ", round(best_cp, 4), "\n")
Best CP: 0.01
Table 4. Cross-validation results for different complexity
parameters.
Figure M. Plot illustrates the error curve.
Although the complexity plot shows an elbow at cp = 0.051, indicating
a point where further splits begin to yield diminishing returns,
cross-validation identified cp = 0.01 as the optimal value for
minimizing prediction error while maintaining a reasonable model
complexity. More specifically, the plot indicates that cp = 0.12 is best
when using the 1-standard error (1-SE) rule.
Pruned Regression
Tree: Best CP (1-SE)
This code selects complexity parameters (cp) for pruning the
regression tree based on both the minimum cross-validation error and the
1-SE rule. It then prunes the tree and makes predictions on the test
set, saving the R-squared and RMSE values for later comparison across
regression models.
# Step 1: Identify the minimum xerror and its corresponding cp (for pruning with minimum error)
cp.table <- tree_model$cptable
min.xerror <- min(cp.table[, "xerror"])
min.cp.row <- which.min(cp.table[, "xerror"])
min.cp <- cp.table[min.cp.row, "CP"]
# Step 2: Compute the standard error of the minimum xerror
xerror.std <- cp.table[min.cp.row, "xstd"]
threshold <- min.xerror + xerror.std
# Step 3: Override best.cp manually to get a more complex tree
best.cp <- 0.012 # based on 1-SE rule, max CP for xerror < 1
# Step 4: Prune the tree using both min.cp and manually specified best.cp
pruned.tree.best.cp <- prune(tree_model, cp = best.cp)
pruned.tree.min.cp <- prune(tree_model, cp = min.cp)
# Step 5: Visualize the manually pruned tree
rpart.plot(pruned.tree.best.cp,
main = paste("Pruned Tree Best CP (1-SE): cp =", round(best.cp, 5)))

# Step 6: Make predictions on test data
pred.best.cp <- predict(pruned.tree.best.cp, newdata = test.data)
pred.min.cp <- predict(pruned.tree.min.cp, newdata = test.data)
# Step 7: Save R-squared values
rsquared.bestcp <- cor(test.data$totChol, pred.best.cp)^2
# Save MSE for best.cp (1-SE rule)
rmse.bestcp <- sqrt(mean((pred.best.cp - test.data$totChol)^2))
Figure N. Pruned Tree Best CP using 1-SE Rule.
Pruned Regression
Tree: Minimum CP
The following tree represents the pruned regression model using the
complexity parameter (CP) that minimizes cross-validation error. This
version of the tree (Minimum CP) aims to balance model complexity and
predictive accuracy by fitting as closely as possible to the training
data without overfitting.
# Visualize the pruned tree using the minimum CP value
rpart.plot(pruned.tree.min.cp,
main = paste("Pruned Regression Tree (Min CP): cp =", round(min.cp, 4)),
type = 2, # Label all nodes with predicted values
extra = 101, # Show fitted values and % of observations at each node
under = TRUE, # Display the split condition underneath the node
faclen = 0, # Avoid abbreviating factor levels
cex = 0.7, # Shrink text for better fit
fallen.leaves = TRUE, # Better layout for the leaves at the bottom
box.palette = "GnBu", # Green-blue color theme for boxes
shadow.col = "gray") # Add shadows to boxes for depth

#r^2 for min.cp
rsquared.mincp <- cor(test.data$totChol, pred.min.cp)^2
#Save MSE for min.cp (minimum xerror)
rmse.mincp <- sqrt(mean((pred.min.cp - test.data$totChol)^2))
Figure O. Pruned Regression Tree, Minimum CP.
The final CART regression model predicting total cholesterol
identified sex and cigarettes per day as the most important predictors.
This suggests that cholesterol levels in the Framingham dataset are most
effectively segmented by gender and smoking behavior.
BAGGING CART
Regression
To further improve predictive performance, we next apply Bagging
(Bootstrap Aggregating) to CART regression. Bagging builds multiple
trees on different bootstrap samples of the data and averages their
predictions, helping to reduce variance and improve model stability
compared to a single decision tree.
To optimize the Bagging CART regression model, we perform a
systematic hyperparameter tuning process. We split the data into
training and testing sets, set up 5-fold cross-validation, and explore
different combinations of the number of bagged trees, complexity
parameters (cp), and maximum tree depths to identify the settings that
minimize out-of-bag (OOB) error.
# Split the data into training and testing sets
set.seed(123)
train.index <- createDataPartition(fhs$totChol, p = 0.8, list = FALSE)
train.data <- fhs[train.index, ]
test.data <- fhs[-train.index, ]
# Set up train control for cross-validation
ctrl <- trainControl(
method = "cv",
number = 5,
verboseIter = TRUE
)
# Define parameter combinations to test
nbagg.values <- c(10, 25, 50) # number of bagged trees
cp.values <- c(0.01, 0.05, 0.1) # candidate cp values for rpart
maxdepth.values <- c(5, 10, 20) # maximum depth of the candidate tree
# Create an empty data frame to store results
results <- data.frame()
# Model tuning loop
for (nbagg in nbagg.values) {
for (cp in cp.values) {
for (maxdepth in maxdepth.values) {
set.seed(123)
model <- bagging(
totChol ~ .,
data = train.data,
nbagg = nbagg,
coob = TRUE,
trControl = ctrl,
control = rpart.control(cp = cp,
maxdepth = maxdepth)
)
# Get OOB error from each iteration
oob.error <- model$err
# Store results
results <- rbind(results,
data.frame(nbagg = nbagg,
cp = cp,
maxdepth = maxdepth,
oob.error = oob.error))
}
}
}
# Find the best combination that yields the minimum out-of-bag error
best.params <- results[which.min(results$oob.error), ]
pander(best.params)
Table 5. Best hyperparameter combination for Bagging CART regression,
showing 50 trees, a complexity parameter (cp) of 0.01, and a maximum
tree depth of 5, resulting in the lowest out-of-bag error (39.09).
The final Bagging CART model is fitted to the training data using the
optimal hyperparameters from the tuning process. Predictions are then
generated on the test set for later evaluation.
# Retrain the final model using the best parameters
set.seed(123)
final.bagging.model <- bagging(
totChol ~ .,
data = train.data,
nbagg = best.params$nbagg,
coob = TRUE,
control = rpart.control(cp = best.params$cp, maxdepth = best.params$maxdepth)
)
# Make predictions on test set
pred.bagging <- predict(final.bagging.model, newdata = test.data)
# Calculate MSE and R-squared
rmse.bagging <- sqrt(mean((pred.bagging - test.data$totChol)^2))
ss.res <- sum((pred.bagging - test.data$totChol)^2)
ss.tot <- sum((mean(train.data$totChol) - test.data$totChol)^2)
rsq.bagging <- 1 - (ss.res / ss.tot)
In the next step, the importance of each predictor in the final
Bagging model is evaluated. This analysis helps identify the features
that have the most significant influence on predicting the target
variable, offering insights into the model’s decision-making
process.
# Extract variable importance using caret's varImp function
var.imp <- varImp(final.bagging.model)
# Custom function to get variable importance from bagging model
get.bagging.importance <- function(model) {
# Get all the trees from the bagging model
trees <- model$mtrees
# Initialize importance vector
imp <- numeric(length(trees[[1]]$btree$variable.importance))
names(imp) <- names(trees[[1]]$btree$variable.importance)
# Sum importance across all trees
for (tree in trees) {
imp[names(tree$btree$variable.importance)] <-
imp[names(tree$btree$variable.importance)] +
tree$btree$variable.importance
}
# Average importance
imp <- imp / length(trees)
return(imp)
}
# Get variable importance from the final bagging model
bagging_importance <- get.bagging.importance(final.bagging.model)
# Display variable importance
#print(bagging_importance)
# Get importance
importance.scores <- get.bagging.importance(final.bagging.model)
# Set custom margins: bottom, left, top, right
par(mar = c(5, 8, 4, 2)) # increases left margin for labels
# Sort and plot
importance.scores <- sort(importance.scores, decreasing = TRUE)
barplot(importance.scores, horiz = TRUE, las = 1,
main = "Variable Importance - Bagging (ipred)",
xlab = "Importance Score")

Figure P. The bar chart displays the relative importance of each
predictor variable in the final Bagging model, with the variables
ordered by their contribution to the model’s predictions.
Random Forest
Regression
Random Forest regression is a powerful ensemble learning technique
that aggregates predictions from multiple decision trees to improve
accuracy and robustness. In the process, hyperparameters such as the
number of trees, the number of variables tried at each split, and the
minimum node size play a crucial role in determining the model’s
performance. The next step involves tuning these hyperparameters to
optimize the Random Forest model for predicting total cholesterol
(totChol) using cross-validation and evaluating the resulting
performance.
Hyperparameter tuning for the Random Forest model involves testing
different combinations of parameters, including mtry, ntree, and
nodesize, to identify the optimal configuration based on RMSE
performance.
# Hyperparameter Tuning for Random Forest
tune_grid <- expand.grid(
mtry = c(2, 4, 6, 8),
ntree = c(100, 200),
nodesize = c(1, 5)
)
tune_results <- list()
for (i in 1:nrow(tune_grid)) {
rf_temp <- randomForest(
totChol ~ .,
data = train.data,
mtry = tune_grid$mtry[i],
ntree = tune_grid$ntree[i],
nodesize = tune_grid$nodesize[i]
)
preds <- predict(rf_temp, newdata = test.data)
rmse <- sqrt(mean((test.data$totChol - preds)^2))
tune_results[[i]] <- c(
rmse = rmse,
mtry = tune_grid$mtry[i],
ntree = tune_grid$ntree[i],
nodesize = tune_grid$nodesize[i]
)
}
# Create data frame and find best parameters
tune_df <- as.data.frame(do.call(rbind, tune_results))
best_params <- tune_df[which.min(tune_df$rmse), ]
After tuning the Random Forest model, a separate Bagging model is
built by setting the number of predictors considered at each split equal
to the total number of available predictors. This approach emphasizes
reducing variance by fully growing each tree and averaging their
predictions for more stable results.
set.seed(123)
# Bagging model: mtry = total number of predictors
bagging_model <- randomForest(
totChol ~ .,
data = train.data,
mtry = ncol(train.data) - 1, # Exclude target variable
importance = TRUE
)
# Predict on test data
bagging_pred <- predict(bagging_model, newdata = test.data)
# Performance: RMSE
bagging_rmse <- sqrt(mean((test.data$totChol - bagging_pred)^2))
#print(paste("Bagging RMSE:", round(bagging_rmse, 2)))
Using the best hyperparameters, the final Random Forest model is
trained on the full training set. Predictions are made on the test data,
and model performance is assessed through RMSE and R-squared values.
# Final Random Forest Model with Best Params
#-------------------------------------------
final_rf <- randomForest(
totChol ~ .,
data = train.data,
mtry = best_params$mtry,
ntree = best_params$ntree,
nodesize = best_params$nodesize,
importance = TRUE
)
# Predict on test data
rf_pred <- predict(final_rf, newdata = test.data)
# Calculate MSE
rmse.rforest <- sqrt(mean((test.data$totChol - rf_pred)^2))
# Calculate R-squared
ss.res <- sum((test.data$totChol - rf_pred)^2)
ss.tot <- sum((mean(train.data$totChol) - test.data$totChol)^2)
rsq.rforest <- 1 - (ss.res / ss.tot)
print(final_rf)
Call:
randomForest(formula = totChol ~ ., data = train.data, mtry = best_params$mtry, ntree = best_params$ntree, nodesize = best_params$nodesize, importance = TRUE)
Type of random forest: regression
Number of trees: 200
No. of variables tried at each split: 4
Mean of squared residuals: 1501.703
% Var explained: 21.07
Table 6. Summary of the final Random Forest model, displaying the
number of trees, selected hyperparameters, out-of-bag (OOB) error
estimate, and variable importance measures.
Variable importance is evaluated for the final Random Forest model to
identify the predictors that contribute most to the prediction of total
cholesterol.
# Variable importance
importance(final_rf)
%IncMSE IncNodePurity
sex 15.1415565 168934.57
age 20.5671015 786271.21
currentSmoker 4.5871529 76396.55
cigsPerDay 11.5185748 329470.56
BPMeds 3.6936359 26805.30
prevalentStroke -0.9200363 11001.93
prevalentHyp 6.0569320 71403.75
diabetes -1.7994060 31643.76
sysBP 10.0326148 692038.42
diaBP 11.4193775 639401.81
BMI 9.2685742 790493.00
heartRate 5.1588655 564034.29
glucose 0.4333843 626173.40
TenYearCHD 0.2739989 85944.16
Cluster 40.7558708 775366.72
varImpPlot(final_rf, main = "Random Forest Variable Importance")
Table 7. Variable importance table.
Figure Q. Variable importance plot.
The variable importance table presents two key metrics: %IncMSE,
which measures the increase in mean squared error when a variable is
randomly permuted, and IncNodePurity, which reflects the total decrease
in node impurity due to splits on each variable. Higher values in either
metric indicate greater influence on the model’s predictive
accuracy.
The accompanying Random Forest Variable Importance Plot visually
ranks the predictors based on these scores, providing an intuitive way
to identify the most impactful features in the model’s performance.
The table of variable importance for the Random Forest model shows
two key metrics: %IncMSE, which indicates how much the mean squared
error increases when each variable is permuted, and IncNodePurity, which
reflects the total decrease in node impurity due to each variable. To
visualize these results, the variable importance plot ranks the features
based on their impact, providing an intuitive view of which variables
are most influential in predicting the target.
Comparison: Model
Performance
In this analysis, various regression models are evaluated to predict
total cholesterol (totChol) using the Framingham Heart Study dataset.
The models’ performance is assessed through key metrics such as Root
Mean Squared Error (RMSE) and R-squared, with the goal of determining
which model most accurately captures the underlying patterns in the
data.
#--- Assumes these values are already computed and stored ---
# mse.bestcp, mse.mincp
# rsquared.bestcp, rsquared.mincp
# mse.bagging, rsq.bagging
# mse.rforest, rsq.rforest
# Calculate LSE model (Stepwise AIC)
model.lse <- step(lm(totChol ~ ., data = train.data), direction = "both", trace = FALSE)
pred.lse <- predict(model.lse, newdata = test.data)
# Compute metrics for LSE model
rmse.lse <- sqrt(mean((test.data$totChol - pred.lse)^2))
ss.res.lse <- sum((test.data$totChol - pred.lse)^2)
ss.tot.lse <- sum((mean(train.data$totChol) - test.data$totChol)^2)
rsq.lse <- 1 - (ss.res.lse / ss.tot.lse)
#--- Create summary table ---
model_results <- data.frame(
Model = c("Pruned Tree (min cp)",
"Pruned Tree (1-SE cp)",
"Bagging",
"Random Forest",
"Stepwise AIC (LSE)"),
RMSE = c(rmse.mincp,
rmse.bestcp,
rmse.bagging,
rmse.rforest,
rmse.lse),
R_squared = c(rsquared.mincp,
rsquared.bestcp,
rsq.bagging,
rsq.rforest,
rsq.lse)
)
# View comparison
print(model_results)
Model RMSE R_squared
1 Pruned Tree (min cp) 37.80819 0.2045056
2 Pruned Tree (1-SE cp) 38.38551 0.1799623
3 Bagging 43.20233 0.1852816
4 Random Forest 42.67037 0.2052216
5 Stepwise AIC (LSE) 43.37807 0.1786397
Table 8. RMSE and R-squared values for the four tree models, with
Stepwise AIC (LSE) as the baseline for comparison.
Random Forest outperformed the other regression models, achieving the
highest R-squared and the lowest RMSE, indicating its superior ability
to capture the relationships within the data. Pruned Tree (min cp)
followed closely behind, showing competitive performance but slightly
lower predictive accuracy compared to Random Forest. cleaned of
redundancy but metric are different than my code
CART
CLASSIFICATION
CART (Classification and Regression Trees) classification constructs
a decision tree that splits the data into increasingly homogeneous
groups based on a categorical outcome, such as the presence or absence
of 10-year coronary heart disease (TenYearCHD) in the Framingham Heart
Study dataset. This approach identifies the most important predictors
and optimal split points to best classify individuals according to their
risk of developing CHD within ten years. The key components for this
analysis are listed below.
- Splitting Criterion: Uses measures like Gini impurity or entropy to
choose the best variable and split point.
- Stopping Rules: Parameters such as minsplit, minbucket, and maxdepth
control tree growth and prevent overfitting.
- Pruning: Reduces tree complexity using the complexity parameter (cp)
and cross-validation to select an optimal subtree.
- Prediction and Evaluation: The final tree is used to predict
outcomes on test data, typically evaluated using accuracy, sensitivity,
specificity, and AUC.
Initial CART
Classification
The CART classification model for predicting TenYearCHD is built
using a subset of the Framingham Heart Study dataset. The tree is
constructed with specific parameters such as limiting the depth of the
tree and ensuring a minimum number of observations for splits. The
resulting model represents the full, unpruned tree, which is then
visualized to show the class probabilities and labels at each node,
providing insights into how different variables contribute to the
prediction of 10-year coronary heart disease risk.
# Set seed for reproducibility
set.seed(123)
# Split data into training (70%) and testing (30%) sets
train_index <- sample(1:nrow(fhs), size = 0.7 * nrow(fhs))
train_data <- fhs[train_index, ]
test_data <- fhs[-train_index, ]
# Build initial CART classification model (predicting TenYearCHD)
class_model <- rpart(
TenYearCHD ~ .,
data = train_data,
method = "class", # Classification tree
control = rpart.control(
minsplit = 10,
minbucket = 5,
cp = 0.0001,
maxdepth = 5
)
)
# Visualize the classification tree
rpart.plot(class_model,
extra = 104, # Show probabilities and class labels
box.palette = "GnBu", # Green to blue gradient
branch.lty = 1,
shadow.col = "gray",
nn = TRUE, # Show node numbers
main = "Initial CART Classification Tree for TenYearCHD")

Figure R. Initial CART Decision Tree.
Tuning
This code evaluates the tuning of a classification tree model by
examining the complexity parameter (cp) values from the model’s
cost-complexity table (cptable). It identifies the cp that corresponds
to the minimum cross-validation error (xerror) and the cp within the
1-standard error (1-SE) range, which helps select the simplest tree that
still provides optimal performance. By tuning the cp, we aim to balance
model complexity and predictive accuracy, with the 1-SE rule serving as
a conservative approach to prevent overfitting. The output provides both
the minimum cp and the cp within the 1-SE range, offering valuable
insights into the trade-off between a more complex model and a simpler,
potentially more generalizable one.
pander(class_model$cptable)
| 0.009461 |
0 |
1 |
1 |
0.04297 |
| 0.008734 |
4 |
0.9607 |
1.013 |
0.0432 |
| 0.004367 |
5 |
0.952 |
1.011 |
0.04316 |
| 0.001456 |
14 |
0.9061 |
1.035 |
0.04358 |
| 1e-04 |
17 |
0.9017 |
1.037 |
0.04361 |
min.row <- which.min(class_model$cptable[, "xerror"])
min.cp <- class_model$cptable[min.row, "CP"]
se <- class_model$cptable[min.row, "xstd"]
xerror.cutoff <- class_model$cptable[min.row, "xerror"] + se
# Now find the simplest (largest cp) tree within 1-SE range
cp.1SE <- max(class_model$cptable[class_model$cptable[, "xerror"] <= xerror.cutoff, "CP"])
cat("min.cp:", min.cp, "\n")
min.cp: 0.009461426
cat("cp.1SE:", cp.1SE, "\n")
cp.1SE: 0.009461426
# Check the number of terminal nodes (leaves) in both trees
#cat("Terminal nodes for pruned.tree.1SE:", length(pruned.tree.1SE$frame$var[pruned.tree.1SE$frame$var == "<leaf>"]), "\n")
#cat("Terminal nodes for pruned.tree.min:", length(pruned.tree.min$frame$var[pruned.tree.min$frame$var == "<leaf>"]), "\n")
# Compare splits between the two trees (structure)
#print(pruned.tree.1SE)
#print(pruned.tree.min)
# Print the structure of the pruned trees to inspect differences
#summary(pruned.tree.1SE)
#summary(pruned.tree.min)
Table 9. Complexity Parameter Table.
The minimum cross-validated error occurred at a complexity parameter
(cp) value of 0.0102. Using the 1-SE rule, the simplest tree within one
standard error of the minimum error also corresponds to the same cp
value of 0.0102. This suggests that no further simplification is
necessary, and the tree with cp = 0.0102 is both the most accurate and
the most parsimonious choice.
The following plot displays the cross-validation results for the
classification tree model, illustrating how the complexity parameter
(cp) influences the model’s cross-validation error.
# Plot the cross-validation results
plotcp(class_model)

Figure S. CART Complexity Parameter (cp) Plot.
The cross-validation plot shows that the lowest error occurs at a
tree size of 5, corresponding to a complexity parameter (cp) of 0.0062,
suggesting that this tree size offers the best balance between model
complexity and predictive performance.
CART Classification
(1-SE Rule)
The pruned CART classification tree using the 1-SE rule is a method
for simplifying the decision tree model by reducing its complexity while
maintaining predictive performance. This approach involves selecting the
smallest tree within one standard error of the minimum cross-validation
error, ensuring a balance between model simplicity and accuracy. Using
the plot, a cp of 0.0062 is used for 1-SE Rule.
# 1. Find the optimal cp value that minimizes cross-validated error
min.cp <- class_model$cptable[which.min(class_model$cptable[, "xerror"]), "CP"]
# 2. Manually specify the 1-SE cp value (you already found this earlier)
cp.1SE <- 0.0062
# 3. Prune the tree using the 1-SE cp value
pruned.tree.1SE <- prune(class_model, cp = cp.1SE)
# 4. Visualize the pruned tree (1-SE rule version)
rpart.plot(pruned.tree.1SE,
extra = 104, # Show predicted class and probability
box.palette = "GnBu", # Green to blue color scheme
branch.lty = 1,
shadow.col = "gray",
nn = TRUE,
main = "Pruned CART Classification Tree (1-SE Rule)")

# 5. Generate predictions for pruned tree (1-SE rule)
pred.prob.1SE <- predict(pruned.tree.1SE, test_data, type = "prob")[, 2]
# 6. Create ROC curve for pruned tree (1-SE rule)
roc.tree.1SE <- pROC::roc(test_data$TenYearCHD, pred.prob.1SE)
# 7. Access the AUC directly from the ROC object
auc.tree.1SE <- roc.tree.1SE$auc
# 8. Optionally, print the AUC
#print(paste("AUC for pruned tree (1-SE):", round(auc.tree.1SE, 4)))
Figure T. Pruned CART Classification tree using 1-SE Rule.
CART Classification
Tree (Min CP)
The pruned CART classification tree is visualized using the minimum
complexity parameter (cp) value, which helps in reducing model
complexity while retaining predictive accuracy. This visualization
highlights the class probabilities and the number of observations at
each node, providing a clearer understanding of the model’s
decision-making process.
# Prune the tree using the cp that gives minimum cross-validated error
pruned.tree.min <- prune(class_model, cp = min.cp)
# Visualize the pruned tree with the chosen minimum cp
rpart.plot(pruned.tree.min,
extra = 104, # Displays more detailed information on nodes (class probabilities, number of observations)
box.palette = "GnBu", # Green to Blue color scheme for node boxes
branch.lty = 1, # Solid lines for branches
shadow.col = "gray", # Shadow color for node labels
nn = TRUE, # Add node numbers to the tree
main = "Pruned CART Classification Tree (Min CP)" # Title for the plot
)

Figure U. Pruned CART Classification tree using min CP.
The pruned CART classification tree using the minimum CP resulted in
a single node, suggesting that this approach did not produce a
meaningful or informative model.
BAGGING Cart
Classification
The BAGGING (Bootstrap Aggregating) method is applied to the CART
(Classification and Regression Trees) model to predict TenYearCHD, a
binary outcome indicating the likelihood of cardiovascular disease
within ten years. BAGGING combines multiple decision trees built on
bootstrapped subsets of the data, reducing variance and improving model
robustness. By averaging the predictions of individual trees, BAGGING
helps mitigate overfitting, making it particularly useful for complex
datasets like the Framingham Heart Study. The analysis explores the
model’s performance through cross-validation, tuning hyperparameters,
and evaluating the results based on RMSE and R-squared, ultimately
identifying the optimal parameters for accurate predictions.
First, the BAGGING model is trained using the chosen training data
and aggregate the predictions from each decision tree to make the final
classification, ensuring that each model’s prediction contributes to the
overall output.
# Set seed for reproducibility
set.seed(123)
# Ensure the target is a factor
fhs$TenYearCHD <- as.factor(fhs$TenYearCHD)
# Split data into training and testing sets
trainIndex <- createDataPartition(fhs$TenYearCHD, p = 0.7, list = FALSE)
trainData <- fhs[trainIndex, ]
testData <- fhs[-trainIndex, ]
# Create hyperparameter grid
hyper.grid <- expand.grid(
nbagg = c(25, 50, 100),
minsplit = c(5, 10, 20),
maxdepth = c(5, 10, 20),
cp = c(0.01, 0.001)
)
# Initialize tracking variables
results <- data.frame()
best.accuracy <- 0
best.params <- list()
# Loop through hyperparameter combinations
for(i in 1:nrow(hyper.grid)) {
params <- hyper.grid[i, ]
ctrl <- rpart.control(
minsplit = params$minsplit,
maxdepth = params$maxdepth,
cp = params$cp
)
# Train Bagging model
bag.model <- bagging(
TenYearCHD ~ .,
data = trainData,
nbagg = params$nbagg,
coob = TRUE,
control = ctrl
)
# Predict on test set
preds <- predict(bag.model, newdata = testData)
# Accuracy
cm <- confusionMatrix(preds, testData$TenYearCHD)
accuracy <- cm$overall["Accuracy"]
# Store results
results <- rbind(results, data.frame(
nbagg = params$nbagg,
minsplit = params$minsplit,
maxdepth = params$maxdepth,
cp = params$cp,
Accuracy = accuracy
))
# Track best result
if(accuracy > best.accuracy) {
best.accuracy <- accuracy
best.params <- params
}
}
# Display best parameters
cat("Best BAGGING hyperparameters:\n")
Best BAGGING hyperparameters:
pander(best.params)
# Optional: View full results
# pander(results)
Table 10. Train final BAGGING Classification model with best
hyperparameters
The output shows the optimal hyperparameters for the BAGGING model,
with 100 trees (nbagg), a minimum split of 20 observations (minsplit), a
maximum depth of 20 for each tree (maxdepth), and a complexity parameter
(cp) of 0.001.
Next the final BAGGING model is trained using the best
hyperparameters identified during tuning. The model is then used to
predict the test data, and the results are evaluated using a labeled
confusion matrix to assess the classification accuracy for predicting
TenYearCHD.
# Set rpart control using best parameters from tuning
best.control <- rpart.control(
minsplit = best.params$minsplit,
maxdepth = best.params$maxdepth,
cp = best.params$cp
)
# Train the final Bagging model on training data
final.bag.model <- bagging(
TenYearCHD ~ .,
data = trainData,
nbagg = best.params$nbagg,
coob = TRUE,
control = best.control
)
# Generate predictions (probabilities) for the Bagging model
pred.bag.prob <- predict(final.bag.model, newdata = testData, type = "prob")[, 2] # [ ,2] selects the probability for the positive class (1)
# Compute ROC for Bagging model
roc.bagging <- roc(testData$TenYearCHD, pred.bag.prob)
# Compute AUC for Bagging model
auc.bagging <- roc.bagging$auc
# Plot ROC curve for the Bagging model
#plot(roc.bagging, main = "ROC Curve - Bagging", col = "blue", lwd = 2)
# Now proceed with comparing other models and plotting as needed
Random Forest
Classification
Random Forest classification is an ensemble learning method that
builds multiple decision trees and aggregates their predictions to
improve classification accuracy. Each tree is trained on a random subset
of the data with random feature selection at each split, which helps
reduce overfitting and increases the model’s robustness. The final
prediction is determined by the majority vote of the individual trees,
making Random Forest a powerful tool for classification tasks,
especially when dealing with complex and high-dimensional data.
Hyperparameter tuning involves selecting the optimal settings for a
model’s parameters, such as the number of trees, the maximum depth of
each tree, and the minimum number of samples required for a split, to
enhance its performance and accuracy.
set.seed(123)
# Drop BPMeds_original from both sets
train.data <- train.data[, !colnames(train.data) %in% "BPMeds_original"]
test.data <- test.data[, !colnames(test.data) %in% "BPMeds_original"]
# Ensure same columns and column order
test.data <- test.data[, names(train.data)]
# Ensure all factor levels are aligned
for (col in names(train.data)) {
if (is.factor(train.data[[col]])) {
test.data[[col]] <- factor(test.data[[col]], levels = levels(train.data[[col]]))
}
}
# Two-way split: training/testing
train.idx <- createDataPartition(fhs$TenYearCHD, p = 0.7, list = FALSE)
train.data <- fhs[train.idx, ]
test.data <- fhs[-train.idx, ]
test.data <- test.data[, names(train.data)]
# Ensure outcome is factor with levels "0", "1"
train.data$TenYearCHD <- as.factor(train.data$TenYearCHD)
test.data$TenYearCHD <- as.factor(test.data$TenYearCHD)
# Ensure 'BPMeds_original' is not in the data
train.data <- train.data[, !colnames(train.data) %in% "BPMeds_original"]
# Cross-validation setup
k <- 5
train.size <- nrow(train.data)
fold.size <- floor(train.size / k)
# Hyperparameter grid
tune.grid <- expand.grid(
mtry = c(2, 3, 4, 5),
ntree = c(100, 300, 500),
nodesize = c(1, 3, 5, 10),
maxnodes = c(5, 10, 20, NA)
)
# Ensure valid mtry values before starting
p <- ncol(train.data) - 1 # Number of predictors
tune.grid <- subset(tune.grid, mtry >= 1 & mtry <= p & nodesize < maxnodes)
# Initialize results storage
results <- data.frame()
best.auc <- 0
best.hyp.params <- NULL
# Grid search with cross-validation
for (i in 1:nrow(tune.grid)) {
current.tune.params <- tune.grid[i, ]
cv.auc <- numeric(k) # Preallocate AUC vector for folds
for (j in 1:k) {
# Define training and validation sets
cv.id <- ((j - 1) * fold.size + 1):(j * fold.size)
cv.train <- train.data[-cv.id, ]
cv.valid <- train.data[cv.id, ]
# Train the model
rf.cv <- randomForest(
TenYearCHD ~ .,
data = cv.train,
mtry = current.tune.params$mtry,
ntree = current.tune.params$ntree,
nodesize = current.tune.params$nodesize,
maxnodes = current.tune.params$maxnodes
)
# Predict probabilities for positive class '1'
prob.cv <- predict(rf.cv, newdata = cv.valid, type = "prob")[, "1"]
# Compute AUC using pROC
roc_obj <- pROC::roc(
response = cv.valid$TenYearCHD,
predictor = prob.cv,
direction = "<"
)
cv.auc[j] <- pROC::auc(roc_obj)
}
# Compute average AUC over folds
avg.auc <- mean(cv.auc)
# Store results
results <- rbind(results, data.frame(
mtry = current.tune.params$mtry,
ntree = current.tune.params$ntree,
nodesize = current.tune.params$nodesize,
maxnodes = current.tune.params$maxnodes,
auc = avg.auc
))
# Update best if current is better
if (avg.auc > best.auc) {
best.auc <- avg.auc
best.hyp.params <- current.tune.params
}
}
# Display best parameters
pander(data.frame(cbind(best.hyp.params, best.auc)))
Table 11. Random Forest Hyperparameter Tuning Results Table
The output indicates the optimal hyperparameters for the Random
Forest model, with mtry set to 4, ntree set to 500, nodesize set to 10,
and maxnodes set to 20. These settings resulted in a best AUC of 0.6965,
which reflects the model’s performance in distinguishing between the
classes.
To finalize the Random Forest model, the best hyperparameters
identified from tuning are used to train the model on the full training
dataset. Predictions for class probabilities of the positive class (“1”)
are then made for the test data. In the subsequent steps, the model’s
performance will be assessed using the ROC curve and AUC, which will be
presented during the comparison section.
# Train the final Random Forest model using best parameters
final.rf.cls <- randomForest(
TenYearCHD ~ .,
data = train.data,
ntree = best.hyp.params$ntree,
mtry = best.hyp.params$mtry,
nodesize = best.hyp.params$nodesize,
maxnodes = best.hyp.params$maxnodes,
importance = TRUE
)
# Predict class probabilities for the positive class "1"
pred.rf.prob <- predict(final.rf.cls, test.data, type = "prob")[, "1"]
# Ensure response is a factor with correct levels
test.response <- factor(test.data$TenYearCHD, levels = c(0, 1))
# Compute ROC and AUC
rf.roc <- pROC::roc(response = test.response, predictor = pred.rf.prob, levels = c("0", "1"), direction = "<")
rf.auc <- pROC::auc(rf.roc)
# Display AUC
#print(rf.auc)
The importance of each predictor variable in the final Random Forest
model is assessed using two metrics: Mean Decrease in Accuracy and Mean
Decrease in Gini. These measures are extracted and visualized to
highlight the most influential variables, providing insights into which
features contribute the most to the model’s predictive power. The
results are displayed in a bar plot that separates the importance scores
for each metric.
# Extract importance measures
importance_vals <- importance(final.rf.cls)
importance_df <- data.frame(
Variable = rownames(importance_vals),
MeanDecreaseAccuracy = importance_vals[, "MeanDecreaseAccuracy"],
MeanDecreaseGini = importance_vals[, "MeanDecreaseGini"]
)
# Pivot for plotting
importance_long <- pivot_longer(
importance_df,
cols = c(MeanDecreaseAccuracy, MeanDecreaseGini),
names_to = "Metric",
values_to = "Importance"
)
# Plot
ggplot(importance_long, aes(x = reorder(Variable, Importance), y = Importance, fill = Metric)) +
geom_bar(stat = "identity", position = "dodge") +
coord_flip() +
facet_wrap(~ Metric, scales = "free_x") +
labs(title = "Variable Importance (Random Forest)",
x = "Variable",
y = "Importance Score") +
theme_minimal() +
theme(legend.position = "none")

Figure V. Random Forest Feature Importance Visualization
Comparison
To evaluate and compare the predictive performance of the pruned CART
models, logistic regression, Bagging, and Random Forest, we generated
ROC (Receiver Operating Characteristic) curves using the test dataset.
These curves illustrate the trade-off between sensitivity and
specificity across all possible classification thresholds, providing a
visual comparison of model performance across different methods.
# Fit the Logistic Regression Model
logit_model <- glm(TenYearCHD ~ ., data = train.data, family = binomial)
# Predict probabilities for the test set
pred.logit.prob <- predict(logit_model, newdata = test.data, type = "response")
# Ensure response is a factor with correct levels
test.response <- factor(test.data$TenYearCHD, levels = c(0, 1))
# Compute ROC and AUC for Logistic Regression
logit.roc <- roc(response = test.response, predictor = pred.logit.prob, levels = c("0", "1"), direction = "<")
logit.auc <- pROC::auc(logit.roc)
# Recompute prediction, ensure it's aligned and un-named
pred.prob.1SE <- predict(pruned.tree.1SE, newdata = test.data, type = "prob")[, 2]
pred.prob.1SE <- as.numeric(pred.prob.1SE) # removes any row names
# ROC for each model (ensure the model probabilities are defined first)
roc.tree.1SE <- roc(test.data$TenYearCHD, pred.prob.1SE)
pred.prob.min <- predict(pruned.tree.min, newdata = test.data, type = "prob")[, 2]
pred.prob.min <- as.numeric(pred.prob.min)
roc.tree.min <- roc(test.data$TenYearCHD, pred.prob.min)
roc.bagging <- roc(test.data$TenYearCHD, pred.bag.prob)
roc.rf <- roc(test.data$TenYearCHD, pred.rf.prob)
roc.logit <- roc(test.data$TenYearCHD, pred.logit.prob)
# AUC values
auc.tree.1SE <- pROC::auc(roc.tree.1SE)
auc.tree.min <- pROC::auc(roc.tree.min)
auc.bagging <- pROC::auc(roc.bagging)
auc.rf <- pROC::auc(roc.rf)
auc.logit <- pROC::auc(roc.logit)
# Plot ROC curves for comparison
par(mar = c(5, 5, 4, 1))
plot(1 - roc.logit$specificities, roc.logit$sensitivities,
type = "l", col = "darkorange", lwd = 2,
xlab = "1 - Specificity", ylab = "Sensitivity",
main = "ROC Curve Comparison")
lines(1 - roc.tree.1SE$specificities, roc.tree.1SE$sensitivities, col = "gold", lwd = 2)
lines(1 - roc.tree.min$specificities, roc.tree.min$sensitivities, col = "purple", lwd = 2)
lines(1 - roc.bagging$specificities, roc.bagging$sensitivities, col = "blue", lwd = 2)
lines(1 - roc.rf$specificities, roc.rf$sensitivities, col = "forestgreen", lwd = 2)
# Add a legend for clarity
legend("bottomright",
legend = c("Logistic", "CART 1SE", "CART Min", "Random Forest", "Bagging"),
col = c("darkorange", "gold", "purple", "blue", "forestgreen"),
lty = c(1, 1, 1, 1, 1),
lwd = 2,
bty = "n",
cex = 0.6)
### === Annotate with AUCs === ###
# Annotating the plot with the AUC values
text(0.75, 0.48, paste("Logistic AUC: ", round(auc.logit, 4)), cex = 0.7)
text(0.75, 0.42, paste("CART 1SE AUC: ", round(auc.tree.1SE, 4)), cex = 0.7)
text(0.75, 0.36, paste("CART Min AUC: ", round(auc.tree.min, 4)), cex = 0.7)
text(0.75, 0.30, paste("Random Forest AUC: ", round(auc.rf, 4)), cex = 0.7)
text(0.75, 0.24, paste("Bagging AUC: ", round(auc.bagging, 4)), cex = 0.7)

Figure W. ROC Curves for Classification Models.
Receiver Operating Characteristic (ROC) Curves for Multiple
Predictive Models: The plot compares the ROC curves for Logistic
Regression, CART with 1-SE pruning, CART with Minimum Cost-Complexity
Pruning, Bagging, and Random Forest. AUC values for each model are
annotated, highlighting their classification performance. Among the
models evaluated, Logistic Regression achieved the highest AUC,
demonstrating superior accuracy in distinguishing between individuals
who will and will not develop coronary heart disease over a ten-year
period. This suggests that, in this scenario, Logistic Regression
provides a more reliable risk prediction tool compared to the other
models.
Optimal Cutoff
The optimal cutoff represents the threshold probability at which the
classification model balances sensitivity and specificity, maximizing
the overall performance of the model. By using this cutoff, we ensure
that predictions are made with the most effective trade-off between
correctly identifying positive cases and minimizing false positives,
which is crucial for making reliable decisions in practical applications
such as healthcare or finance.
# Find the optimal cutoff using Youden's J statistic
coords.optimal <- coords(roc.logit, x = "best", best.method = "youden", ret = c("threshold", "sensitivity", "specificity"))
# Print the optimal cutoff and associated sensitivity/specificity
print(coords.optimal)
threshold sensitivity specificity
1 0.1563719 0.6321244 0.703154
Table 12. The optimal cutoff.
The threshold value of 0.1563719 is the optimal cutoff. It is chosen
to balance the sensitivity and specificity. If you are using this
threshold, the model will classify a person as at risk for coronary
heart disease if their predicted probability is greater than 15.6%. A
sensitivity of 63.2% indicates that the model is fairly good at
identifying individuals who will develop coronary heart disease.
However, it misses about 36.8% of the true positives (false negatives).
On the other hand, the specificity of 70.3% indicates that the model
does a better job of correctly identifying individuals who will not
develop the disease, though there is still some room for improvement
(about 29.7% false positives).
LS0tCnRpdGxlOiAiTW9kZWxpbmcgQ29yb25hcnkgSGVhcnQgRGlzZWFzZSBSaXNrIGFuZCBDaG9sZXN0ZXJvbCBMZXZlbHM6IEEgTWFjaGluZSBMZWFybmluZyBBcHByb2FjaCB3aXRoIHRoZSBGcmFtaW5naGFtIEhlYXJ0IFN0dWR5IgphdXRob3I6ICJKb2FubmUgTXVzYSIKZGF0ZTogIkFwcmlsIDIwMjUiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIHRvY19mbG9hdDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdG9jX2NvbGxhcHNlZDogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgc21vb3RoX3Njcm9sbDogeWVzCiAgICB0aGVtZTogbHVtZW4KICB3b3JkX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGtlZXBfbWQ6IHllcwogIHBkZl9kb2N1bWVudDogCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgZmlnX3dpZHRoOiAzCiAgICBmaWdfaGVpZ2h0OiAzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBgez1odG1sfQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCi8qIENhc2NhZGluZyBTdHlsZSBTaGVldHMgKENTUykgaXMgYSBzdHlsZXNoZWV0IGxhbmd1YWdlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHByZXNlbnRhdGlvbiBvZiBhIGRvY3VtZW50IHdyaXR0ZW4gaW4gSFRNTCBvciBYTUwuIGl0IGlzIGEgc2ltcGxlIG1lY2hhbmlzbSBmb3IgYWRkaW5nIHN0eWxlIChlLmcuLCBmb250cywgY29sb3JzLCBzcGFjaW5nKSB0byBXZWIgZG9jdW1lbnRzLiAqLwoKaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8KICBmb250LXNpemU6IDIycHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgY29sb3I6IERhcmtSZWQ7CiAgdGV4dC1hbGlnbjogY2VudGVyOwogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsKfQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGF1dGhvcnMgICovCiAgZm9udC1zaXplOiAxOHB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7CiAgY29sb3I6IG5hdnk7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9Cmg0LmRhdGUgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIHRoZSBkYXRlICAqLwogIGZvbnQtc2l6ZTogMThweDsKICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogIGNvbG9yOiBEYXJrQmx1ZTsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8KICAgIGZvbnQtc2l6ZTogMjJweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgICBmb250LXdlaWdodDogYm9sZDsKfQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovCiAgICBmb250LXNpemU6IDIwcHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwp9CgpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDE4cHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9CgouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQoKcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0KCjwvc3R5bGU+CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CgojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLgoKaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikKICAgbGlicmFyeShrbml0cikKfQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCmxpYnJhcnkodGlkeXZlcnNlKQp9CgppZiAoIXJlcXVpcmUoImUxMDcxIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiZTEwNzEiKQpsaWJyYXJ5KGUxMDcxKQp9CmlmICghcmVxdWlyZSgibW1lbG4iKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJtbWVsbiIpCmxpYnJhcnkobW1lbG4pCn0KaWYgKCFyZXF1aXJlKCJNQVNTIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiTUFTUyIpCmxpYnJhcnkoTUFTUykKfQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKbGlicmFyeShnZ3Bsb3QyKQp9CmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikKbGlicmFyeShwbG90bHkpCn0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikKbGlicmFyeShjYXJldCkKfQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpCmxpYnJhcnkocGFuZGVyKQp9CmlmICghcmVxdWlyZSgicmFuZG9tRm9yZXN0IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygicmFuZG9tRm9yZXN0IikKbGlicmFyeShyYW5kb21Gb3Jlc3QpCn0KaWYgKCFyZXF1aXJlKCJycGFydCIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInJwYXJ0IikKbGlicmFyeShycGFydCkKfQppZiAoIXJlcXVpcmUoInJwYXJ0LnBsb3QiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IikKbGlicmFyeShycGFydC5wbG90KQp9CmlmICghcmVxdWlyZSgicFJPQyIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInBST0MiKQpsaWJyYXJ5KHBST0MpCn0KCmlmICghcmVxdWlyZSgiY29ycnBsb3QiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJjb3JycGxvdCIpCmxpYnJhcnkoY29ycnBsb3QpCn0KCmlmICghcmVxdWlyZSgiZ2xtbmV0IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiZ2xtbmV0IikKbGlicmFyeShnbG1uZXQpCn0KCmlmICghcmVxdWlyZSgiTWV0cmljcyIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoIk1ldHJpY3MiKQpsaWJyYXJ5KE1ldHJpY3MpCn0KCmlmICghcmVxdWlyZSgibWljZSIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoIm1pY2UiKQpsaWJyYXJ5KG1pY2UpCn0KCmlmICghcmVxdWlyZSgiaXByZWQiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJpcHJlZCIpCmxpYnJhcnkobWljZSkKfQoKCgojIwprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApICAKYGBgCgoKIyBJTlRST0RVQ1RJT046IEZSQU1JTkdIQU0gREFUQSBTVUJTRVQKClRoZSBGcmFtaW5naGFtIEhlYXJ0IFN0dWR5LCBpbml0aWF0ZWQgaW4gMTk0OCwgd2FzIGRlc2lnbmVkIHRvIGlkZW50aWZ5IHRoZSBmYWN0b3JzIGFuZCBjaGFyYWN0ZXJpc3RpY3MgdGhhdCBjb250cmlidXRlIHRvIHRoZSBkZXZlbG9wbWVudCBvZiBjYXJkaW92YXNjdWxhciBkaXNlYXNlLiBCYXNlZCBpbiBNYXNzYWNodXNldHRzLCB0aGUgc3R1ZHkgaGFzIGZvbGxvd2VkIG11bHRpcGxlIGdlbmVyYXRpb25zIG9mIHBhcnRpY2lwYW50cyB1c2luZyBhIGxvbmdpdHVkaW5hbCBkZXNpZ24gdGhhdCBpbmNsdWRlcyByZWd1bGFyIGV4YW1pbmF0aW9ucyBhbmQgaGVhbHRoIGFzc2Vzc21lbnRzLiBQYXJ0aWNpcGFudHMgdW5kZXJnbyBjb21wcmVoZW5zaXZlIG1lZGljYWwgZXZhbHVhdGlvbnMsIGxhYm9yYXRvcnkgdGVzdGluZywgYW5kIGxpZmVzdHlsZSBhc3Nlc3NtZW50cy4gVGhlIHN0dWR5IGNhcHR1cmVzIGEgd2lkZSByYW5nZSBvZiByaXNrIGZhY3RvcnMsIGluY2x1ZGluZyBib3RoIGJlaGF2aW9yYWwgYW5kIGJpb2xvZ2ljYWwgY29tcG9uZW50cy4KClRoaXMgYW5hbHlzaXMgZm9jdXNlcyBvbiBhIHN1YnNldCBvZiBkYXRhIGZyb20gdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkuIFNwZWNpZmljYWxseSwgaXQgaW52ZXN0aWdhdGVzIGhvdyBzZWxlY3RlZCBiaW9sb2dpY2FsIGFuZCBiZWhhdmlvcmFsIHJpc2sgZmFjdG9ycyBwcmVkaWN0IHRoZSBsaWtlbGlob29kIG9mIGRldmVsb3BpbmcgY29yb25hcnkgaGVhcnQgZGlzZWFzZSAoQ0hEKSB3aXRoaW4gYSB0ZW4teWVhciBwZXJpb2QuCgoKIyMgRGVzY3JpcHRpb24gb2YgRGF0YQoKVGhlIG9yaWdpbmFsIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkgaW5jbHVkZXMgZGF0YSBmcm9tIGFwcHJveGltYXRlbHkgMTUsMDAwIHBhcnRpY2lwYW50cyBhbmQgb3ZlciAxLDAwMCB2YXJpYWJsZXMuIEZvciB0aGlzIGFuYWx5c2lzLCBhIHN1YnNldCBvZiB0aGUgZGF0YSBpcyB1c2VkLCBjb250YWluaW5nIDQsMjQwIG9ic2VydmF0aW9ucyBhbmQgMTYgdmFyaWFibGVzLgoKVGhpcyBhbmFseXNpcyBmb2N1c2VzIHNwZWNpZmljYWxseSBvbiBiaW9sb2dpY2FsIGZlYXR1cmVzIHRoYXQgZGlyZWN0bHkgY29udHJpYnV0ZSB0byBoZWFsdGggb3V0Y29tZXMuIFByaW9yIGFuYWx5c2lzIChodHRwczovL2ptdXNhMy5naXRodWIuaW8vam11c2EvcHJvamVjdC9Qcm9qZWN0XzIuaHRtbCkgaW5kaWNhdGVkIHRoYXQgZWR1Y2F0aW9uIHdhcyBub3QgYSBzaWduaWZpY2FudCBwcmVkaWN0b3Igb2YgZWl0aGVyIFRlbi1ZZWFyIENvcm9uYXJ5IEhlYXJ0IERpc2Vhc2UgKENIRCkgcmlzayBvciB0b3RhbCBjaG9sZXN0ZXJvbCBsZXZlbHMsIHN1cHBvcnRpbmcgaXRzIHJlbW92YWwgZnJvbSB0aGUgZmVhdHVyZSBzZXQuIEFzIHN1Y2gsIHRoZSB2YXJpYWJsZSDigJxFZHVjYXRpb27igJ0gaGFzIGJlZW4gZXhjbHVkZWQgZnJvbSB0aGUgZGF0YXNldC4KClRoZSBvcmlnaW5hbCBmZWF0dXJlICJtYWxlIiBpcyBjaGFuZ2VkIHRvICJzZXgiIGZvciBlYXNlIG9mIGNvbXByZWhlbnNpb24uIEFmdGVyIGV4Y2x1ZGluZyDigJxFZHVjYXRpb24s4oCdIHRoZSBkYXRhc2V0IGZvciB0aGlzIGFuYWx5c2lzIGNvbnNpc3RzIG9mIDE1IHByZWRpY3RvciB2YXJpYWJsZXM6CgoJ4oCiCTcgZmFjdG9yIHZhcmlhYmxlcyAoY2F0ZWdvcmljYWwpCgnigKIJOCBudW1lcmljIHZhcmlhYmxlcyAoY29udGludW91cykKClRoZSBuYW1lcyBhbmQgZGVmaW5pdGlvbnMgb2YgdGhlc2UgdmFyaWFibGVzIGFyZSBwcm92aWRlZCBiZWxvdy4KCjEuIHNleCAoZmFjdG9yKTogdGhlIHNleCBvZiB0aGUgb2JzZXJ2YXRpb25zOiBmZW1hbGUgKDApLCBtYWxlICgxKS4KMi4gYWdlIChudW1lcmljYWwpOiBBZ2UgYXQgdGhlIHRpbWUgb2YgbWVkaWNhbCBleGFtaW5hdGlvbiBpbiB5ZWFycy4KMy4gY3VycmVudFNtb2tlciAoZmFjdG9yKTogQ3VycmVudCBjaWdhcmV0dGUgc21va2luZyBhdCB0aGUgdGltZSBvZiBleGFtaW5hdGlvbnMgCjQuIGNpZ3NQZXJEYXkgKG51bWVyaWNhbCk6IE51bWJlciBvZiBjaWdhcmV0dGVzIHNtb2tlZCBlYWNoIGRheSAKNS4gQlBtZWRzIChmYWN0b3IpOiBVc2Ugb2YgQW50aS1oeXBlcnRlbnNpdmUgbWVkaWNhdGlvbiBhdCBleGFtIAo2LiBwcmV2YWxlbnRTdHJva2UgKGZhY3Rvcik6IFByZXZhbGVudCBTdHJva2UgKDAgPSBmcmVlIG9mIGRpc2Vhc2UpIAo3LiBwcmV2YWxlbnRIeXAgKGZhY3Rvcik6IFByZXZhbGVudCBIeXBlcnRlbnNpdmUuIFN1YmplY3Qgd2FzIGRlZmluZWQgYXMgaHlwZXJ0ZW5zaXZlIGlmIHRyZWF0ZWQgCjguIGRpYWJldGVzIChmYWN0b3IpOiBEaWFiZXRpYyBhY2NvcmRpbmcgdG8gY3JpdGVyaWEgb2YgZmlyc3QgZXhhbSB0cmVhdGVkIAo5LiB0b3RDaG9sIChudW1lcmljYWwpOiBUb3RhbCBjaG9sZXN0ZXJvbCAobWcvZEwpIAoxMC4gc3lzQlAgKG51bWVyaWNhbCk6IFN5c3RvbGljIEJsb29kIFByZXNzdXJlIChtbUhnKSAKMTEuIGRpYUJQIChudW1lcmljYWwpOiBEaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUgKG1tSGcpIAoxMi4gQk1JIChudW1lcmljYWwpOiBCb2R5IE1hc3MgSW5kZXgsIHdlaWdodCAoa2cpL2hlaWdodCAobSleMiAKMTMuIGhlYXJ0UmF0ZSAobnVtZXJpY2FsKTogSGVhcnQgcmF0ZSAoYmVhdHMvbWludXRlKSAKMTQuIGdsdWNvc2UgKG51bWVyaWNhbCk6IEJsb29kIGdsdWNvc2UgbGV2ZWwgKG1nL2RMKSAKMTUuIFRlblllYXJDSEQgKGZhY3RvciwgcmVzcG9uc2UgdmFyaWFibGUpOiBUaGUgMTAgeWVhciByaXNrIG9mIGNvcm9uYXJ5IGhlYXJ0IGRpc2Vhc2UoQ0hEKS4KClRoZSBkYXRhIHNldCBoYXMgbWlzc2luZyB2YWx1ZXMgZm9yIHNpeCB2YXJpYWJsZXM6IGNpZ1BlckRheSwgQlBNZWRzLCB0b3RDaG9sLCBCTUksIGhlYXJ0UmF0ZSwgYW5kIGdsdWNvc2UuIAoKYGBge3J9CgpmaHMgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9qbXVzYTMvam11c2EvcmVmcy9oZWFkcy9tYWluL0ZyYW1pbmdoYW1IZWFydFN0dWR5LmNzdiIpCgpmaHMgPC0gZmhzICU+JSBkcGx5cjo6c2VsZWN0KC1lZHVjYXRpb24pCgojIGNoYW5nZSAnbWFsZScgdG8gJ3NleCcgd2hlcmUgMSA9IG1hbGUgYW5kIDAgPSBmZW1hbGUKZmhzIDwtIGZocyAlPiUKICByZW5hbWUoc2V4ID0gbWFsZSkKZmhzJHNleCA8LSBmYWN0b3IoZmhzJHNleCkKZmhzJEJQTWVkcyA8LSBmYWN0b3IoZmhzJEJQTWVkcykKZmhzJGN1cnJlbnRTbW9rZXIgPC0gZmFjdG9yKGZocyRjdXJyZW50U21va2VyKQpmaHMkcHJldmFsZW50U3Ryb2tlIDwtIGZhY3RvcihmaHMkcHJldmFsZW50U3Ryb2tlKQpmaHMkcHJldmFsZW50SHlwIDwtIGZhY3RvcihmaHMkcHJldmFsZW50SHlwKQpmaHMkZGlhYmV0ZXMgPC0gZmFjdG9yKGZocyRkaWFiZXRlcykKCnN1bW1hcnkoZmhzKQojaGVhZChmaHMpCmBgYAoKVGFibGUgMS4gU3VtbWFyeSBzdGF0aXN0aWNzIGZvciB0aGUgMTUgZmVhdHVyZXMgaW4gdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkgc3Vic2V0LiBTaXggb2YgdGhlIHZhcmlhYmxlcyBoYXZlIG1pc3NpbmcgdmFsdWVzLiAKCgoKIyBFREEKCiMjIFNpbmdsZSBGZWF0dXJlcwoKQWxsIG9mIHRoZSBmZWF0dXJlcyBpbiB0aGlzIGRhdGEgc2V0IGFyZSB0eXBlZCBhcyBudW1lcmljYWwuIEluIHR1cm4sIGluZGljYXRvciB2YXJpYWJsZXMgYXJlIGNoYW5nZWQgdG8gZmFjdG9yLiBGb3IgdmlzdWFsaXphdGlvbiwgIm1hbGUiIGlzIGNoYW5nZWQgdG8gInNleCIuIFRoZSBpbmRpY2F0b3IgdmFsdWVzIGZvciB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHdlcmUgY2hhbmdlZCB0byBkZXNjcmlwdGl2ZSB3b3JkcyBvciBwaHJhc2VzIHRvIGVuaGFuY2UgcmVhZGFiaWxpdHkgb2YgdGhlIGdyYXBocy4gCgoKYGBge3J9CgojIFNldCB1cCBwbG90dGluZyBhcmVhCnBhcihtZnJvdyA9IGMoMiwgMikpCgojIEhpc3RvZ3JhbSBmb3IgYWdlCmhpc3QoZmhzJGFnZSwKICAgICBtYWluID0gIkFnZSIsCiAgICAgeGxhYiA9ICJBZ2UiLAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLAogICAgIGJvcmRlciA9ICJ3aGl0ZSIpCgojIEhpc3RvZ3JhbSBmb3IgY2lnc1BlckRheQpoaXN0KGZocyRjaWdzUGVyRGF5LAogICAgIG1haW4gPSAiQ2lnYXJldHRlcyBQZXIgRGF5IiwKICAgICB4bGFiID0gIkNpZ2FyZXR0ZXMgUGVyIERheSIsCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgYm9yZGVyID0gIndoaXRlIikKCiMgSGlzdG9ncmFtIGZvciB0b3RDaG9sCmhpc3QoZmhzJHRvdENob2wsCiAgICAgbWFpbiA9ICJUb3RhbCBDaG9sZXN0ZXJvbCIsCiAgICAgeGxhYiA9ICJUb3RhbCBDaG9sZXN0ZXJvbCIsCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgYm9yZGVyID0gIndoaXRlIikKCiMgSGlzdG9ncmFtIGZvciBzeXNCUApoaXN0KGZocyRzeXNCUCwKICAgICBtYWluID0gIlN5c3RvbGljIEJsb29kIFByZXNzdXJlIiwKICAgICB4bGFiID0gIlN5c3RvbGljIEJQIiwKICAgICBjb2wgPSAic3RlZWxibHVlIiwKICAgICBib3JkZXIgPSAid2hpdGUiKQoKIyBSZXNldCBwbG90dGluZyBsYXlvdXQgdG8gZGVmYXVsdApwYXIobWZyb3cgPSBjKDEsIDEpKQoKCgpgYGAKCkZpZ3VyZSBBLiBIaXN0b2dyYW1zIG9mIGFnZSwgQ2lnc1BlckRheSwgdG90Q2hvbCAocmVzcG9uc2UpLCBhbmQgc3lzQlAuIAoKCgoKYGBge3J9CiMgU2V0IHVwIDJ4MiBwbG90dGluZyBhcmVhCnBhcihtZnJvdyA9IGMoMiwgMikpCgojIEhpc3RvZ3JhbSBmb3IgZGlhc3RvbGljIGJsb29kIHByZXNzdXJlCmhpc3QoZmhzJGRpYUJQLAogICAgIG1haW4gPSAiRGlhc3RvbGljIEJsb29kIFByZXNzdXJlIiwKICAgICB4bGFiID0gIkRpYXN0b2xpYyBCUCIsCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgYm9yZGVyID0gIndoaXRlIikKCiMgSGlzdG9ncmFtIGZvciBCTUkKaGlzdChmaHMkQk1JLAogICAgIG1haW4gPSAiQm9keSBNYXNzIEluZGV4IChCTUkpIiwKICAgICB4bGFiID0gIkJNSSIsCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgYm9yZGVyID0gIndoaXRlIikKCiMgSGlzdG9ncmFtIGZvciBoZWFydCByYXRlCmhpc3QoZmhzJGhlYXJ0UmF0ZSwKICAgICBtYWluID0gIkhlYXJ0IFJhdGUiLAogICAgIHhsYWIgPSAiQmVhdHMgUGVyIE1pbnV0ZSIsCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgYm9yZGVyID0gIndoaXRlIikKCiMgSGlzdG9ncmFtIGZvciBnbHVjb3NlCmhpc3QoZmhzJGdsdWNvc2UsCiAgICAgbWFpbiA9ICJHbHVjb3NlIExldmVsIiwKICAgICB4bGFiID0gIkdsdWNvc2UiLAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLAogICAgIGJvcmRlciA9ICJ3aGl0ZSIpCgojIFJlc2V0IHRvIGRlZmF1bHQgcGxvdHRpbmcgbGF5b3V0CnBhcihtZnJvdyA9IGMoMSwgMSkpCgpgYGAKCkZpZ3VyZSBCLiBIaXN0b2dyYW1zIGZvciBkaWFCUCwgQk1JLCBoZWFydFJhdGUsIGFuZCBnbHVjb3NlLiAKCgoKYGBge3J9CgojIFNldCB1cCAyeDIgcGxvdHRpbmcgbGF5b3V0CnBhcihtZnJvdyA9IGMoMiwgMikpCgoKIyBCYXIgcGxvdCBmb3Igc2V4CmJhcnBsb3QodGFibGUoZmhzJHNleCksCiAgICAgICAgbWFpbiA9ICJTZXggRGlzdHJpYnV0aW9uIiwKICAgICAgICBjb2wgPSAic3RlZWxibHVlIiwKICAgICAgICBuYW1lcy5hcmcgPSBjKCJGZW1hbGUiLCAiTWFsZSIpLAogICAgICAgIHlsYWIgPSAiQ291bnQiKQoKCgojIEJhciBwbG90IGZvciBjdXJyZW50IHNtb2tlcgpiYXJwbG90KHRhYmxlKGZocyRjdXJyZW50U21va2VyKSwKICAgICAgICBtYWluID0gIkN1cnJlbnQgU21va2VyIFN0YXR1cyIsCiAgICAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgICAgbmFtZXMuYXJnID0gYygiTm8iLCAiWWVzIiksCiAgICAgICAgeWxhYiA9ICJDb3VudCIpCgojIEJhciBwbG90IGZvciBCUCBtZWRzCmJhcnBsb3QodGFibGUoZmhzJEJQTWVkcyksCiAgICAgICAgbWFpbiA9ICJCbG9vZCBQcmVzc3VyZSBNZWRpY2F0aW9uIiwKICAgICAgICBjb2wgPSAic3RlZWxibHVlIiwKICAgICAgICBuYW1lcy5hcmcgPSBjKCJObyIsICJZZXMiKSwKICAgICAgICB5bGFiID0gIkNvdW50IikKCgoKYGBgCgpGaWd1cmUgQy4gQmFyIGNoYXJ0cyBmb3Igc2V4LCBjdXJyZW50U21va2VyIGFuZCBCUE1lZHMuIAoKCmBgYHtyfQoKIyBTZXQgMngyIGxheW91dCBmb3IgYmFyIGNoYXJ0cwpwYXIobWZyb3cgPSBjKDIsIDIpKQoKIyBCYXIgY2hhcnQgZm9yIHByZXZhbGVudFN0cm9rZQpiYXJwbG90KHRhYmxlKGZocyRwcmV2YWxlbnRTdHJva2UpLAogICAgICAgIG1haW4gPSAiUHJldmFsZW50IFN0cm9rZSIsCiAgICAgICAgeGxhYiA9ICIwID0gTm8sIDEgPSBZZXMiLAogICAgICAgIGNvbCA9ICJzdGVlbGJsdWUiKQoKIyBCYXIgY2hhcnQgZm9yIHByZXZhbGVudEh5cApiYXJwbG90KHRhYmxlKGZocyRwcmV2YWxlbnRIeXApLAogICAgICAgIG1haW4gPSAiUHJldmFsZW50IEh5cGVydGVuc2lvbiIsCiAgICAgICAgeGxhYiA9ICIwID0gTm8sIDEgPSBZZXMiLAogICAgICAgIGNvbCA9ICJzdGVlbGJsdWUiKQoKIyBCYXIgY2hhcnQgZm9yIGRpYWJldGVzCmJhcnBsb3QodGFibGUoZmhzJGRpYWJldGVzKSwKICAgICAgICBtYWluID0gIkRpYWJldGVzIiwKICAgICAgICB4bGFiID0gIjAgPSBObywgMSA9IFllcyIsCiAgICAgICAgY29sID0gInN0ZWVsYmx1ZSIpCgojIEJhciBjaGFydCBmb3IgVGVuWWVhckNIRApiYXJwbG90KHRhYmxlKGZocyRUZW5ZZWFyQ0hEKSwKICAgICAgICBtYWluID0gIlRlbi1ZZWFyIENIRCIsCiAgICAgICAgeGxhYiA9ICIwID0gTm8sIDEgPSBZZXMiLAogICAgICAgIGNvbCA9ICJzdGVlbGJsdWUiKQoKYGBgCgpGaWd1cmUgRC4gQmFyIGNoYXJ0cyBmb3IgcHJldmFsZW50U3Ryb2tlLCBwcmV2YWxlbnRIeXAsIGRpYWJldGVzLCBhbmQgVGVuWWVhckNIRCAocmVzcG9uc2UpLiAKCgojIE1JU1NJTkcgVkFMVUVTCgoKSW1wdXRhdGlvbiBpcyBhIGNyaXRpY2FsIHN0ZXAgaW4gZGF0YSBwcmVwcm9jZXNzaW5nLCBlc3BlY2lhbGx5IHdoZW4gd29ya2luZyB3aXRoIGRhdGFzZXRzIGNvbnRhaW5pbmcgbWlzc2luZyB2YWx1ZXMuIEl0IGludm9sdmVzIGVzdGltYXRpbmcgYW5kIHJlcGxhY2luZyBtaXNzaW5nIGRhdGEgd2l0aCBwbGF1c2libGUgdmFsdWVzIGJhc2VkIG9uIHRoZSBhdmFpbGFibGUgaW5mb3JtYXRpb24uCgpGb3IgdGhlIG1pc3NpbmcgY2lnc1BlckRheSB2YWx1ZXMsIGFueSBpbnN0YW5jZXMgdGhhdCBhcmUgbm90IGN1cnJlbnQgc21va2VycyAoY3VycmVudFNtb2tlciA9IDApIGFuZCBoYXZlIG1pc3NpbmcgY2lnc1BlckRheSB3aWxsIGJlIGltcHV0ZWQgd2l0aCB6ZXJvLiBBbGwgb3RoZXIgaW5zdGFuY2VzIHdpdGggbWlzc2luZyBjaWdzUGVyRGF5IHdpbGwgYmUgaW1wdXRlZCB3aXRoIHRoZSBtZWFuLgoKCmBgYHtyfQoKCiMgU3RlcCAxOiBTYXZlIHRoZSBvcmlnaW5hbCBjaWdzUGVyRGF5IGJlZm9yZSBpbXB1dGF0aW9uCmNpZ3NfYmVmb3JlIDwtIGZocyRjaWdzUGVyRGF5CgojIFN0ZXAgMjogSW1wdXRlIHVzaW5nIGN1cnJlbnRTbW9rZXIKbWVhbl9jaWdzX3Ntb2tlcnMgPC0gbWVhbihmaHMkY2lnc1BlckRheVtmaHMkY3VycmVudFNtb2tlciA9PSAxICYgIWlzLm5hKGZocyRjaWdzUGVyRGF5KV0sIG5hLnJtID0gVFJVRSkKZmhzJGNpZ3NQZXJEYXlbaXMubmEoZmhzJGNpZ3NQZXJEYXkpICYgZmhzJGN1cnJlbnRTbW9rZXIgPT0gMF0gPC0gMApmaHMkY2lnc1BlckRheVtpcy5uYShmaHMkY2lnc1BlckRheSkgJiBmaHMkY3VycmVudFNtb2tlciA9PSAxXSA8LSBtZWFuX2NpZ3Nfc21va2VycwoKIyBTdGVwIDM6IENyZWF0ZSBhIGNvbWJpbmVkIGRhdGEgZnJhbWUgZm9yIHBsb3R0aW5nCmNpZ3NfZGYgPC0gcmJpbmQoCiAgZGF0YS5mcmFtZSh2YWx1ZSA9IGNpZ3NfYmVmb3JlLCBncm91cCA9ICJCZWZvcmUiKSwKICBkYXRhLmZyYW1lKHZhbHVlID0gZmhzJGNpZ3NQZXJEYXksIGdyb3VwID0gIkFmdGVyIikKKQoKIyBTdGVwIDQ6IFBsb3QgdGhlIG92ZXJsYXkgZGVuc2l0eSBjdXJ2ZQpnZ3Bsb3QoY2lnc19kZiwgYWVzKHggPSB2YWx1ZSwgZmlsbCA9IGdyb3VwLCBjb2xvciA9IGdyb3VwKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSkgKwogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIGNpZ3NQZXJEYXkgQmVmb3JlIGFuZCBBZnRlciBJbXB1dGF0aW9uIiwKICAgICAgIHggPSAiQ2lnYXJldHRlcyBQZXIgRGF5IiwgeSA9ICJEZW5zaXR5IikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkJlZm9yZSIgPSAibGlnaHRibHVlIiwgIkFmdGVyIiA9ICJzYWxtb24iKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJCZWZvcmUiID0gImxpZ2h0Ymx1ZSIsICJBZnRlciIgPSAic2FsbW9uIikpICsKICB0aGVtZV9taW5pbWFsKCkKCgoKYGBgCgpGaWd1cmUgRS4gUG9zdCBpbXB1dGF0aW9uLCBjaWdzUGVyRGF5IGhhcyB6ZXJvIG1pc3NpbmcgdmFsdWVzLiBUaGUgYmVmb3JlIGFuZCBhZnRlciBpbXB1dGF0aW9uIGRpc3RyaWJ1dGlvbnMgYXJlIHZlcnkgc2ltaWxhci4KClRoZSBkaXN0cmlidXRpb25zIG9mIENpZ3NQZXJEYXkgYmVmb3JlIGFuZCBhZnRlciBpbXB1dGF0aW9uIGFyZSB2ZXJ5IHNpbWlsYXIsIGluZGljYXRpbmcgdGhhdCB0aGUgaW1wdXRhdGlvbiBwcm9jZXNzIGVmZmVjdGl2ZWx5IHByZXNlcnZlZCB0aGUgb3JpZ2luYWwgZGF0YSBjaGFyYWN0ZXJpc3RpY3MuCgoKTXVsdGlwbGUgaW1wdXRhdGlvbiBieSBjaGFpbmVkIGVxdWF0aW9ucyAoTUlDRSkgaXMgdXNlZCB0byBpbXB1dGUgbWlzc2luZyB2YWx1ZXMgZm9yIGNpZ1BlckRheSwgQlBNZWRzLCB0b3RDaG9sLCBCTUksIGhlYXJ0UmF0ZSwgYW5kIGdsdWNvc2UuIE1JQ0UgaXMgYW4gaXRlcmF0aXZlIG1ldGhvZCB0aGF0IGNyZWF0ZXMgbXVsdGlwbGUgaW1wdXRlZCBkYXRhc2V0cywgaW1wcm92aW5nIHRoZSByZWxpYWJpbGl0eSBvZiB0aGUgaW1wdXRhdGlvbiBwcm9jZXNzIGJ5IGluY29ycG9yYXRpbmcgdW5jZXJ0YWludHkgaW4gdGhlIG1pc3NpbmcgdmFsdWVzLiBUaGUgaW1wdXRlZCB2YWx1ZXMgYXJlIGV4cGVjdGVkIHRvIHByb3ZpZGUgYSBtb3JlIGNvbXBsZXRlIGRhdGFzZXQgZm9yIHN1YnNlcXVlbnQgYW5hbHlzaXMsIHN1Y2ggYXMgbW9kZWxpbmcgYW5kIHByZWRpY3Rpb24uCgpgYGB7cn0KCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmb3IgdGhlIGZlYXR1cmVzIGJlZm9yZSBpbXB1dGF0aW9uIChvcmlnaW5hbCBkYXRhKQpmaHNfbWlzc2luZ19iZWZvcmUgPC0gZmhzWywgYygiY2lnc1BlckRheSIsICJ0b3RDaG9sIiwgIkJNSSIsICJoZWFydFJhdGUiLCAiZ2x1Y29zZSIpXQoKIyBQZXJmb3JtIE1JQ0UgaW1wdXRhdGlvbiB3aGlsZSBmdWxseSBzdXBwcmVzc2luZyBjb25zb2xlIG91dHB1dAppbnZpc2libGUoY2FwdHVyZS5vdXRwdXQoewogIG1pY2VfaW1wdXRhdGlvbiA8LSBtaWNlKGZoc19taXNzaW5nX2JlZm9yZSwgbWV0aG9kID0gJ3BtbScsIG0gPSA1LCBtYXhpdCA9IDUwLCBzZWVkID0gNTAwKQp9KSkKCiMgQ29tcGxldGUgdGhlIGltcHV0ZWQgZGF0YQpmaHNfaW1wdXRlZCA8LSBjb21wbGV0ZShtaWNlX2ltcHV0YXRpb24sIGFjdGlvbiA9IDEpCgojIFByZXBhcmUgZGF0YSBmb3IgcGxvdHRpbmcgKGJlZm9yZSBhbmQgYWZ0ZXIpCmZoc19iZWZvcmUgPC0gZGF0YS5mcmFtZSh2YWx1ZSA9IGFzLnZlY3Rvcihhcy5tYXRyaXgoZmhzX21pc3NpbmdfYmVmb3JlKSksCiAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9IHJlcChjb2xuYW1lcyhmaHNfbWlzc2luZ19iZWZvcmUpLCBlYWNoID0gbnJvdyhmaHNfbWlzc2luZ19iZWZvcmUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFzZXQgPSByZXAoIkJlZm9yZSIsIG5yb3coZmhzX21pc3NpbmdfYmVmb3JlKSAqIG5jb2woZmhzX21pc3NpbmdfYmVmb3JlKSkpCgpmaHNfYWZ0ZXIgPC0gZGF0YS5mcmFtZSh2YWx1ZSA9IGFzLnZlY3Rvcihhcy5tYXRyaXgoZmhzX2ltcHV0ZWQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPSByZXAoY29sbmFtZXMoZmhzX2ltcHV0ZWQpLCBlYWNoID0gbnJvdyhmaHNfaW1wdXRlZCkpLAogICAgICAgICAgICAgICAgICAgICAgICBkYXRhc2V0ID0gcmVwKCJBZnRlciIsIG5yb3coZmhzX2ltcHV0ZWQpICogbmNvbChmaHNfaW1wdXRlZCkpKQoKIyBDb21iaW5lIHRoZSBkYXRhIGZvciBiZWZvcmUgYW5kIGFmdGVyIGludG8gb25lIGRhdGEgZnJhbWUKZmhzX2NvbWJpbmVkIDwtIHJiaW5kKGZoc19iZWZvcmUsIGZoc19hZnRlcikKCiMgQ3JlYXRlIHRoZSBkZW5zaXR5IHBsb3Qgd2l0aCBnZ3Bsb3QKZ2dwbG90KGZoc19jb21iaW5lZCwgYWVzKHggPSB2YWx1ZSwgZmlsbCA9IGRhdGFzZXQsIGNvbG9yID0gZGF0YXNldCkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjUpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArCiAgbGFicyh0aXRsZSA9ICJEZW5zaXR5IFBsb3RzOiBCZWZvcmUgYW5kIEFmdGVyIEltcHV0YXRpb24iLCAKICAgICAgIHggPSAiVmFsdWUiLCB5ID0gIkRlbnNpdHkiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJCZWZvcmUiID0gImxpZ2h0Ymx1ZSIsICJBZnRlciIgPSAicGluayIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkJlZm9yZSIgPSAibGlnaHRibHVlIiwgIkFmdGVyIiA9ICJwaW5rIikpCgoKYGBgCkZpZ3VyZSBGLiBCZWZvcmUgYW5kIGFmdGVyIGltcHV0YXRpb24gZGVuc2l0eSBjdXJ2ZXMuIAoKClRoZSBkaXN0cmlidXRpb25zIG9mIHRoZSB2YXJpYWJsZXMgYmVmb3JlIGFuZCBhZnRlciBpbXB1dGF0aW9uIGFyZSB2ZXJ5IHNpbWlsYXIsIGluZGljYXRpbmcgdGhhdCB0aGUgaW1wdXRhdGlvbiBwcm9jZXNzIGVmZmVjdGl2ZWx5IHByZXNlcnZlZCB0aGUgb3JpZ2luYWwgZGF0YSBjaGFyYWN0ZXJpc3RpY3MuIFRoZSB1c2Ugb2YgbXVsdGlwbGUgaW1wdXRhdGlvbiBieSBjaGFpbmVkIGVxdWF0aW9ucyAoTUlDRSkgaGFzIGVuc3VyZWQgdGhhdCB0aGUgaW1wdXRlZCB2YWx1ZXMgY2xvc2VseSBmb2xsb3cgdGhlIHBhdHRlcm5zIGFuZCBzcHJlYWQgb2YgdGhlIG9ic2VydmVkIGRhdGEsIG1haW50YWluaW5nIHRoZSBpbnRlZ3JpdHkgb2YgdGhlIHZhcmlhYmxlcyBmb3IgZnVydGhlciBhbmFseXNpcy4gSW4gdHVybiwgdGhlIGltcHV0ZWQgdmFsdWVzIGlzIGFkZGVkIHRvIHRoZSBmaW5hbCB3b3JraW5nIGRhdGFzZXQgZm9yIGFuYWx5c2lzLiAKCgoKYGBge3J9CiMgT3ZlcndyaXRlIG9yaWdpbmFsIGNvbHVtbnMgaW4gZmhzIHdpdGggaW1wdXRlZCB2YWx1ZXMKZmhzJGNpZ3NQZXJEYXkgPC0gZmhzX2ltcHV0ZWQkY2lnc1BlckRheQpmaHMkdG90Q2hvbCA8LSBmaHNfaW1wdXRlZCR0b3RDaG9sCmZocyRCTUkgPC0gZmhzX2ltcHV0ZWQkQk1JCmZocyRoZWFydFJhdGUgPC0gZmhzX2ltcHV0ZWQkaGVhcnRSYXRlCmZocyRnbHVjb3NlIDwtIGZoc19pbXB1dGVkJGdsdWNvc2UKCgoKYGBgCgoKClByaW9yIHRvIG1vZGVsaW5nLCB0aGUgdmFyaWFibGUgQlBNZWRzICh3aGV0aGVyIHRoZSBwYXJ0aWNpcGFudCB3YXMgb24gYmxvb2QgcHJlc3N1cmUgbWVkaWNhdGlvbikgY29udGFpbnMgbWlzc2luZyB2YWx1ZXMuIFRvIGFkZHJlc3MgdGhpcywgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIGlzIGZpdHRlZCB1c2luZyByZWxldmFudCBwcmVkaWN0b3JzIChhZ2UsIHN5c0JQLCBkaWFCUCwgY2lnc1BlckRheSwgYW5kIHByZXZhbGVudEh5cCkgYmFzZWQgb24gY2FzZXMgd2l0aCBvYnNlcnZlZCBCUE1lZHMgdmFsdWVzLiBQcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBmcm9tIHRoaXMgbW9kZWwgd2VyZSB0aGVuIHVzZWQgdG8gaW1wdXRlIG1pc3NpbmcgdmFsdWVzLCB3aXRoIGEgdGhyZXNob2xkIG9mIDAuNSB0byBjbGFzc2lmeSBpbmRpdmlkdWFscyBhcyBlaXRoZXIgdGFraW5nIG9yIG5vdCB0YWtpbmcgYmxvb2QgcHJlc3N1cmUgbWVkaWNhdGlvbi4KCmBgYHtyfQoKIyBTYXZlIG9yaWdpbmFsIEJQTWVkcyB3aXRoIE5BcyBiZWZvcmUgaW1wdXRhdGlvbgpmaHMkQlBNZWRzX29yaWdpbmFsIDwtIGZocyRCUE1lZHMKCiMgS2VlcCByb3dzIHdoZXJlIEJQTWVkcyBpcyBub3QgbWlzc2luZwpicG1fY29tcGxldGUgPC0gZmhzWyFpcy5uYShmaHMkQlBNZWRzX29yaWdpbmFsKSwgXQoKIyBGaXQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbApicG1fbW9kZWwgPC0gZ2xtKEJQTWVkc19vcmlnaW5hbCB+IGFnZSArIHN5c0JQICsgZGlhQlAgKyBjaWdzUGVyRGF5ICsgcHJldmFsZW50SHlwLAogICAgICAgICAgICAgICAgIGRhdGEgPSBicG1fY29tcGxldGUsIGZhbWlseSA9IGJpbm9taWFsKQoKIyBJZGVudGlmeSByb3dzIHdpdGggbWlzc2luZyBCUE1lZHMKYnBtX21pc3NpbmcgPC0gZmhzW2lzLm5hKGZocyRCUE1lZHNfb3JpZ2luYWwpLCBdCgojIElkZW50aWZ5IHJvd3MgYW1vbmcgYnBtX21pc3NpbmcgdGhhdCBoYXZlIGNvbXBsZXRlIHByZWRpY3RvcnMKcHJlZGljdGFibGVfcm93cyA8LSBjb21wbGV0ZS5jYXNlcyhicG1fbWlzc2luZ1ssIGMoImFnZSIsICJzeXNCUCIsICJkaWFCUCIsICJjaWdzUGVyRGF5IiwgInByZXZhbGVudEh5cCIpXSkKCiMgUHJlZGljdCBvbmx5IG9uIHJvd3Mgd2UgY2FuIHByZWRpY3QKaWYgKGFueShwcmVkaWN0YWJsZV9yb3dzKSkgewogIHByZWRpY3RlZF9wcm9icyA8LSBwcmVkaWN0KGJwbV9tb2RlbCwgbmV3ZGF0YSA9IGJwbV9taXNzaW5nW3ByZWRpY3RhYmxlX3Jvd3MsIF0sIHR5cGUgPSAicmVzcG9uc2UiKQogIAogICMgR2V0IHRoZSBvcmlnaW5hbCByb3cgbnVtYmVycyBmb3IgdGhlIG1pc3NpbmcgZGF0YQogIG1pc3Npbmdfcm93X2luZGljZXMgPC0gd2hpY2goaXMubmEoZmhzJEJQTWVkc19vcmlnaW5hbCkpCiAgCiAgIyBGaW5kIGluZGljZXMgdGhhdCBhcmUgcHJlZGljdGFibGUKICBwcmVkaWN0YWJsZV9pbmRpY2VzIDwtIG1pc3Npbmdfcm93X2luZGljZXNbcHJlZGljdGFibGVfcm93c10KICAKICAjIEltcHV0ZSBwcmVkaWN0ZWQgdmFsdWVzIGludG8gdGhlIGNvcnJlY3Qgcm93cwogIGZocyRCUE1lZHNbcHJlZGljdGFibGVfaW5kaWNlc10gPC0gaWZlbHNlKHByZWRpY3RlZF9wcm9icyA+PSAwLjUsIDEsIDApCn0KCgojIE1ha2Ugc3VyZSBmaHMkQlBNZWRzIGFuZCBmaHMkQlBNZWRzX29yaWdpbmFsIGFyZSB0aGVyZQoKIyBDcmVhdGUgYSBsb25nLWZvcm1hdCBkYXRhIGZyYW1lCmJwbV9wbG90X2RhdGEgPC0gZGF0YS5mcmFtZSgKICB2YWx1ZSA9IGMoZmhzJEJQTWVkc19vcmlnaW5hbCwgZmhzJEJQTWVkcyksCiAgc3RhdHVzID0gcmVwKGMoIkJlZm9yZSBJbXB1dGF0aW9uIiwgIkFmdGVyIEltcHV0YXRpb24iKSwgZWFjaCA9IG5yb3coZmhzKSkKKQoKIyBDbGVhbiBiYXIgcGxvdApnZ3Bsb3QoYnBtX3Bsb3RfZGF0YSwgYWVzKHggPSBmYWN0b3IodmFsdWUpLCBmaWxsID0gc3RhdHVzKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIikgKwogIGxhYnModGl0bGUgPSAiQlBNZWRzIEJlZm9yZSBhbmQgQWZ0ZXIgSW1wdXRhdGlvbiIsCiAgICAgICB4ID0gIkJQTWVkcyAoMCA9IE5vLCAxID0gWWVzKSIsCiAgICAgICB5ID0gIkNvdW50IiwKICAgICAgIGZpbGwgPSAiU3RhdHVzIikgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygiMCIgPSAiTm8iLCAiMSIgPSAiWWVzIikpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE0KQoKYGBgCgpGaWd1cmUgRy4gU2lkZSBieSBzaWRlIGJhciBjaGFydCBvZiBiZWZvcmUgYW5kIGFmdGVyIGxvZ2lzdGljIGltcHV0YXRpb24uCgpUaGUgZGlzdHJpYnV0aW9uIG9mIEJQTWVkcyBiZWZvcmUgYW5kIGFmdGVyIGxvZ2lzdGljIGltcHV0YXRpb24gaXMgdmVyeSBzaW1pbGFyLCBpbmRpY2F0aW5nIHRoYXQgdGhlIGltcHV0YXRpb24gcHJvY2VzcyBwcmVzZXJ2ZWQgdGhlIHVuZGVybHlpbmcgc3RydWN0dXJlIG9mIHRoZSBkYXRhIHdpdGhvdXQgaW50cm9kdWNpbmcgc3Vic3RhbnRpYWwgYmlhcy4KCmBgYHtyfQoKIyBSZW1vdmUgQlBNZWRzX29yaWdpbmFsIGNvbHVtbiBhZnRlciBpbXB1dGF0aW9uIGFuZCBwbG90dGluZwpmaHMkQlBNZWRzX29yaWdpbmFsIDwtIE5VTEwKCmBgYAoKCiMgRkVBVFVSRSBTRUxFQ1RJT04KCgojIyBGZWF0dXJlIFNlbGVjdGlvbgoKVG8gcmVkdWNlIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzIGluY2x1ZGVkIGluIHBhaXJ3aXNlIHJlbGF0aW9uc2hpcCBwbG90cywgZmVhdHVyZSBzZWxlY3Rpb24gd2FzIHBlcmZvcm1lZCB1c2luZyBhIFJhbmRvbSBGb3Jlc3QgcmVncmVzc2lvbiBtb2RlbC4gVGhlIGdvYWwgaXMgdG8gaWRlbnRpZnkgdGhlIG1vc3QgaW1wb3J0YW50IG51bWVyaWNhbCBwcmVkaWN0b3JzIG9mIFRlblllYXJDSEQsIHNvIHRoYXQgb25seSB0aGUgbW9zdCByZWxldmFudCB2YXJpYWJsZXMgYXJlIGV4YW1pbmVkIGluIHNjYXR0ZXJwbG90cyBhbmQgb3RoZXIgdmlzdWFsaXphdGlvbnMuCgoKYGBge3J9CgoKIyBTdWJzZXQgbnVtZXJpYyBwcmVkaWN0b3JzCm51bWVyaWNfdmFycyA8LSBzYXBwbHkoZmhzLCBpcy5udW1lcmljKQpmaHNfbnVtZXJpYyA8LSBmaHNbLCBudW1lcmljX3ZhcnNdCgojIE1ha2Ugc3VyZSBUZW5ZZWFyQ0hEIGlzIGluY2x1ZGVkCmZoc19udW1lcmljJFRlblllYXJDSEQgPC0gZmhzJFRlblllYXJDSEQgIAoKIyBSYW5kb20gRm9yZXN0IGZvciBudW1lcmljIGZlYXR1cmVzCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpyZl9udW1lcmljIDwtIHJhbmRvbUZvcmVzdChUZW5ZZWFyQ0hEIH4gLiwgZGF0YSA9IGZoc19udW1lcmljLCBpbXBvcnRhbmNlID0gVFJVRSkKCiMgRXh0cmFjdCBpbXBvcnRhbmNlCmltcG9ydGFuY2VfbnVtZXJpYyA8LSBpbXBvcnRhbmNlKHJmX251bWVyaWMpCgojIENyZWF0ZSBpbXBvcnRhbmNlIGRhdGFmcmFtZSBiYXNlZCBvbiBJbmNOb2RlUHVyaXR5CmltcG9ydGFuY2VfbnVtZXJpY19kZiA8LSBkYXRhLmZyYW1lKAogIFZhcmlhYmxlID0gcm93bmFtZXMoaW1wb3J0YW5jZV9udW1lcmljKSwKICBJbXBvcnRhbmNlID0gaW1wb3J0YW5jZV9udW1lcmljWywgIkluY05vZGVQdXJpdHkiXQopCgojIFNvcnQgYnkgaW1wb3J0YW5jZQppbXBvcnRhbmNlX251bWVyaWNfZGYgPC0gaW1wb3J0YW5jZV9udW1lcmljX2RmW29yZGVyKC1pbXBvcnRhbmNlX251bWVyaWNfZGYkSW1wb3J0YW5jZSksIF0KCiMgVmlldyByZXN1bHRzCnByaW50KGltcG9ydGFuY2VfbnVtZXJpY19kZikKCmBgYAoKVGFibGUgMi4gTW9zdCBpbXBvcnRhbnQgbnVtZXJpYyBmZWF0dXJlcy4gCgoKVXNpbmcgYSBSYW5kb20gRm9yZXN0IHJlZ3Jlc3Npb24gbW9kZWwsIGZlYXR1cmUgaW1wb3J0YW5jZSB3YXMgZXZhbHVhdGVkIGZvciB0aGUgbnVtZXJpY2FsIHByZWRpY3RvcnMgb2YgVGVuWWVhckNIRC4gVGhlIHJlc3VsdHMgaW5kaWNhdGUgdGhhdCBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSAoc3lzQlApLCBib2R5IG1hc3MgaW5kZXggKEJNSSksIGFuZCB0b3RhbCBjaG9sZXN0ZXJvbCAodG90Q2hvbCkgYXJlIHRoZSBtb3N0IGluZmx1ZW50aWFsIHZhcmlhYmxlcyBpbiBwcmVkaWN0aW5nIHRlbi15ZWFyIGNvcm9uYXJ5IGhlYXJ0IGRpc2Vhc2Ugcmlzay4gT3RoZXIgbm90YWJsZSBwcmVkaWN0b3JzIGluY2x1ZGUgZ2x1Y29zZSwgYWdlLCBkaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUgKGRpYUJQKSwgYW5kIGhlYXJ0IHJhdGUsIHdoaWxlIGNpZ2FyZXR0ZXMgcGVyIGRheSAoY2lnc1BlckRheSkgaGFkIHRoZSBsb3dlc3QgcmVsYXRpdmUgaW1wb3J0YW5jZSBhbW9uZyB0aGUgbnVtZXJpY2FsIGZlYXR1cmVzIGNvbnNpZGVyZWQuCgoKCgpUbyBpZGVudGlmeSB0aGUgbW9zdCBpbXBvcnRhbnQgY2F0ZWdvcmljYWwgcHJlZGljdG9ycyBvZiBUZW5ZZWFyQ0hELCBhIFJhbmRvbSBGb3Jlc3QgY2xhc3NpZmljYXRpb24gbW9kZWwgd2FzIGZpdCB1c2luZyBvbmx5IHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQuIFRoaXMgZmVhdHVyZSBzZWxlY3Rpb24gc3RlcCBoZWxwcyBmb2N1cyBwYWlyd2lzZSBhbmFseXNlcyBvbiB0aGUgbW9zdCByZWxldmFudCBjYXRlZ29yaWNhbCBmYWN0b3JzLCBpbXByb3ZpbmcgdGhlIGNsYXJpdHkgYW5kIGludGVycHJldGFiaWxpdHkgb2YgdGhlIHZpc3VhbGl6YXRpb25zLgoKCmBgYHtyfQoKIyBTdWJzZXQgb25seSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMKZmFjdG9yX3ZhcnMgPC0gc2FwcGx5KGZocywgaXMuZmFjdG9yKQpmaHNfZmFjdG9yIDwtIGZoc1ssIGZhY3Rvcl92YXJzXQoKCiMgQ2hlY2sgZm9yIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBmaHNfZmFjdG9yIGRhdGFzZXQKI2NvbFN1bXMoaXMubmEoZmhzX2ZhY3RvcikpCgojIFJlbW92ZSBCUE1lZHNfb3JpZ2luYWwgaWYgaXQgZXhpc3RzCmZoc19mYWN0b3IkQlBNZWRzX29yaWdpbmFsIDwtIE5VTEwKCiMgRW5zdXJlIHRoZSB0YXJnZXQgdmFyaWFibGUgVGVuWWVhckNIRCBpcyBhIGZhY3RvciBpbiB0aGUgb3JpZ2luYWwgZGF0YQpmaHMkVGVuWWVhckNIRCA8LSBhcy5mYWN0b3IoZmhzJFRlblllYXJDSEQpCgojIEFkZCBUZW5ZZWFyQ0hEIGJhY2sgdG8gdGhlIGZhY3RvciBkYXRhc2V0IGlmIGl0IHdhcyBkcm9wcGVkCmZoc19mYWN0b3IkVGVuWWVhckNIRCA8LSBmaHMkVGVuWWVhckNIRAoKIyBUcmFpbiBSYW5kb20gRm9yZXN0IGNsYXNzaWZpZXIgb24gY2F0ZWdvcmljYWwgZmVhdHVyZXMKcmZfZmFjdG9yIDwtIHJhbmRvbUZvcmVzdChUZW5ZZWFyQ0hEIH4gLiwgZGF0YSA9IGZoc19mYWN0b3IsIGltcG9ydGFuY2UgPSBUUlVFKQoKIyBHZXQgaW1wb3J0YW5jZSB2YWx1ZXMKaW1wb3J0YW5jZV9mYWN0b3IgPC0gaW1wb3J0YW5jZShyZl9mYWN0b3IpCgojIENvbnZlcnQgdG8gYSBjbGVhbiBkYXRhIGZyYW1lCmltcG9ydGFuY2VfZmFjdG9yX2RmIDwtIGRhdGEuZnJhbWUoCiAgVmFyaWFibGUgPSByb3duYW1lcyhpbXBvcnRhbmNlX2ZhY3RvciksCiAgSW1wb3J0YW5jZSA9IGltcG9ydGFuY2VfZmFjdG9yWywgIk1lYW5EZWNyZWFzZUdpbmkiXQopCgojIFNvcnQgYnkgaW1wb3J0YW5jZQppbXBvcnRhbmNlX2ZhY3Rvcl9kZiA8LSBpbXBvcnRhbmNlX2ZhY3Rvcl9kZltvcmRlcigtaW1wb3J0YW5jZV9mYWN0b3JfZGYkSW1wb3J0YW5jZSksIF0KCiMgUHJpbnQKcHJpbnQoaW1wb3J0YW5jZV9mYWN0b3JfZGYpCgpgYGAKClRhYmxlIDMuIE1vc3QgaW1wb3J0YW50IGNhdGVnb3JpY2FsIGZlYXR1cmVzLgoKClVzaW5nIGEgUmFuZG9tIEZvcmVzdCBjbGFzc2lmaWNhdGlvbiBtb2RlbCwgZmVhdHVyZSBpbXBvcnRhbmNlIHdhcyBldmFsdWF0ZWQgZm9yIHRoZSBjYXRlZ29yaWNhbCBwcmVkaWN0b3JzIG9mIFRlblllYXJDSEQuIFRoZSByZXN1bHRzIHNob3cgdGhhdCBwcmV2YWxlbnQgaHlwZXJ0ZW5zaW9uIChwcmV2YWxlbnRIeXApIGlzIGJ5IGZhciB0aGUgbW9zdCBpbmZsdWVudGlhbCBjYXRlZ29yaWNhbCB2YXJpYWJsZSwgZm9sbG93ZWQgYnkgZGlhYmV0ZXMgYW5kIHNleC4gT3RoZXIgY2F0ZWdvcmljYWwgZmVhdHVyZXMgc3VjaCBhcyBwcmV2YWxlbnQgc3Ryb2tlLCB1c2Ugb2YgYmxvb2QgcHJlc3N1cmUgbWVkaWNhdGlvbiAoQlBNZWRzKSwgYW5kIGN1cnJlbnQgc21va2luZyBzdGF0dXMgaGFkIGxvd2VyIHJlbGF0aXZlIGltcG9ydGFuY2UgaW4gcHJlZGljdGluZyB0ZW4teWVhciBjb3JvbmFyeSBoZWFydCBkaXNlYXNlIHJpc2suCgoKCiMgUEFJUldJU0UgUkVMQVRJT05TSElQUwoKVG8gZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSBtb3N0IGltcG9ydGFudCBudW1lcmljYWwgcHJlZGljdG9ycyBvZiBUZW5ZZWFyQ0hELCBwYWlyd2lzZSBzY2F0dGVycGxvdHMgYXJlIGdlbmVyYXRlZCBmb3IgdGhlIHRvcCBmaXZlIHZhcmlhYmxlcyBpZGVudGlmaWVkIHRocm91Z2ggUmFuZG9tIEZvcmVzdCBmZWF0dXJlIHNlbGVjdGlvbi4gVGhlc2UgaW5jbHVkZSBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSAoc3lzQlApLCBib2R5IG1hc3MgaW5kZXggKEJNSSksIHRvdGFsIGNob2xlc3Rlcm9sICh0b3RDaG9sKSwgZ2x1Y29zZSwgYW5kIGFnZS4gQW5hbHl6aW5nIHRoZXNlIHBhaXJ3aXNlIHJlbGF0aW9uc2hpcHMgaGVscHMgdG8gdmlzdWFsbHkgdW5kZXJzdGFuZCBob3cgdGhlc2UgZmVhdHVyZXMgY29ycmVsYXRlIHdpdGggZWFjaCBvdGhlciBhbmQgd2l0aCB0aGUgdGFyZ2V0IHZhcmlhYmxlLCBUZW5ZZWFyQ0hELiAKCgpgYGB7cn0KCiMgU2VsZWN0IHRoZSB0b3AgNSBtb3N0IGltcG9ydGFudCBudW1lcmljYWwgZmVhdHVyZXMKdG9wX2ZlYXR1cmVzIDwtIGZoc1ssIGMoInN5c0JQIiwgIkJNSSIsICJ0b3RDaG9sIiwgImdsdWNvc2UiLCAiYWdlIildCgojIENvbXB1dGUgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeApjb3JfbWF0cml4IDwtIGNvcih0b3BfZmVhdHVyZXMpCgojIFByaW50IHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggd2l0aCBudW1iZXJzCiNwcmludChjb3JfbWF0cml4KQoKIyBDcmVhdGUgYSBjbGVhbiBjb3JyZWxhdGlvbiBwbG90IHdpdGggbnVtYmVycyBkaXNwbGF5ZWQgaW5zaWRlIHRoZSBjZWxscwpjb3JycGxvdChjb3JfbWF0cml4LCAKICAgICAgICAgbWV0aG9kID0gImNvbG9yIiwgICAgICAgICAjIENvbG9yLWJhc2VkIHZpc3VhbGl6YXRpb24KICAgICAgICAgdHlwZSA9ICJmdWxsIiwgICAgICAgICAgICAjIEZ1bGwgbWF0cml4IGRpc3BsYXkKICAgICAgICAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJyZWQiLCAid2hpdGUiLCAiYmx1ZSIpKSgyMDApLCAjIENvbG9yIGdyYWRpZW50CiAgICAgICAgIHRsLmNleCA9IDAuOCwgICAgICAgICAgICAgIyBBZGp1c3QgbGFiZWwgc2l6ZQogICAgICAgICBudW1iZXIuY2V4ID0gMC44LCAgICAgICAgICMgQWRqdXN0IGNvcnJlbGF0aW9uIG51bWJlciBzaXplCiAgICAgICAgIGFkZENvZWYuY29sID0gImJsYWNrIiwgICAgIyBBZGQgY29ycmVsYXRpb24gY29lZmZpY2llbnRzIGluIGJsYWNrCiAgICAgICAgIHRpdGxlID0gIkNvcnJlbGF0aW9uIE1hdHJpeCBmb3IgVG9wIDUgTnVtZXJpY2FsIEZlYXR1cmVzIiwgCiAgICAgICAgIG1hciA9IGMoMCwwLDEsMCkpICAgICAgICAgIyBNYXJnaW5zIGFyb3VuZCB0aGUgcGxvdApgYGAKCkZpZ3VyZSBILiBDb3JyZWxhdGlvbiBtYXRyaXggZm9yIGNvbnRpbnVvdXMgZmVhdHVyZXMuIAoKCgpUaGUgc2NhdHRlcnBsb3QgbWF0cml4IGJlbG93IGRpc3BsYXlzIHBhaXJ3aXNlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGUgZml2ZSBtb3N0IGltcG9ydGFudCBudW1lcmljYWwgZmVhdHVyZXMgaWRlbnRpZmllZCBmb3IgcHJlZGljdGluZyBjb3JvbmFyeSBoZWFydCBkaXNlYXNlLiBUaGVzZSBmZWF0dXJlcyBpbmNsdWRlIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIChzeXNCUCksIGJvZHkgbWFzcyBpbmRleCAoQk1JKSwgdG90YWwgY2hvbGVzdGVyb2wgKHRvdENob2wpLCBnbHVjb3NlLCBhbmQgYWdlLiBUaGUgc2NhdHRlcnBsb3RzIHByb3ZpZGUgYSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb2YgaG93IHRoZXNlIHZhcmlhYmxlcyByZWxhdGUgdG8gb25lIGFub3RoZXIsIHdpdGggZWFjaCBwbG90IHNob3djYXNpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkYXRhIHBvaW50cyBiZXR3ZWVuIHR3byBmZWF0dXJlcy4gVGhlIGluY2x1c2lvbiBvZiBib290c3RyYXBwaW5nIHJlZHVjZXMgY2x1dHRlciBieSBzYW1wbGluZyAzMCUgb2YgdGhlIGRhdGEsIGFsbG93aW5nIGZvciBjbGVhcmVyIGluc2lnaHRzIGludG8gcG90ZW50aWFsIHBhdHRlcm5zIGFuZCBhc3NvY2lhdGlvbnMuCgpgYGB7cn0KCgojIExvYWQgbmVjZXNzYXJ5IGxpYnJhcmllcwpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ3JpZEV4dHJhKQoKIyBTZWxlY3QgdGhlIHRvcCA1IG1vc3QgaW1wb3J0YW50IG51bWVyaWNhbCBmZWF0dXJlcwp0b3BfZmVhdHVyZXMgPC0gZmhzWywgYygic3lzQlAiLCAiQk1JIiwgInRvdENob2wiLCAiZ2x1Y29zZSIsICJhZ2UiKV0KCiMgQm9vdHN0cmFwIHRoZSBkYXRhIChyZWR1Y2Ugc2l6ZSB0byAzMCUgdG8gZGVjbHV0dGVyIHNjYXR0ZXJwbG90cykKc2V0LnNlZWQoMTIzKSAgIyBGb3IgcmVwcm9kdWNpYmlsaXR5CnRvcF9mZWF0dXJlc19zYW1wbGUgPC0gdG9wX2ZlYXR1cmVzW3NhbXBsZSgxOm5yb3codG9wX2ZlYXR1cmVzKSwgMC4zICogbnJvdyh0b3BfZmVhdHVyZXMpKSwgXQoKIyBDcmVhdGUgdGhlIHNjYXR0ZXJwbG90IG1hdHJpeCBtYW51YWxseQpwYWlyc19saXN0IDwtIGxpc3QoKQoKIyBMb29wIG92ZXIgYWxsIHBhaXJzIG9mIGZlYXR1cmVzIHRvIGNyZWF0ZSBpbmRpdmlkdWFsIHNjYXR0ZXJwbG90cwpmb3IoaSBpbiAxOihuY29sKHRvcF9mZWF0dXJlc19zYW1wbGUpLTEpKSB7CiAgZm9yKGogaW4gKGkrMSk6bmNvbCh0b3BfZmVhdHVyZXNfc2FtcGxlKSkgewogICAgcGFpcnNfbGlzdFtbcGFzdGUobmFtZXModG9wX2ZlYXR1cmVzX3NhbXBsZSlbaV0sIG5hbWVzKHRvcF9mZWF0dXJlc19zYW1wbGUpW2pdLCBzZXAgPSAiXyIpXV0gPC0gCiAgICAgIGdncGxvdCh0b3BfZmVhdHVyZXNfc2FtcGxlLCBhZXNfc3RyaW5nKHggPSBuYW1lcyh0b3BfZmVhdHVyZXNfc2FtcGxlKVtpXSwgeSA9IG5hbWVzKHRvcF9mZWF0dXJlc19zYW1wbGUpW2pdKSkgKwogICAgICBnZW9tX3BvaW50KGFscGhhID0gMC4zLCBzaXplID0gMC41KSArIAogICAgICBsYWJzKHRpdGxlID0gcGFzdGUobmFtZXModG9wX2ZlYXR1cmVzX3NhbXBsZSlbaV0sICJ2cyIsIG5hbWVzKHRvcF9mZWF0dXJlc19zYW1wbGUpW2pdKSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUsIHZqdXN0ID0gMCksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KQogICAgICApCiAgfQp9CgojIEFycmFuZ2UgYWxsIHNjYXR0ZXJwbG90cyBpbiBhIGdyaWQgbGF5b3V0CmdyaWQuYXJyYW5nZShncm9icyA9IHBhaXJzX2xpc3QsIG5jb2wgPSA1KSAgIyBBcnJhbmdlIGluIDUgY29sdW1ucywgYWRqdXN0IG5jb2wgaWYgbmVlZGVkCgpgYGAKCkZpZ3VyZSBJLiBTY2F0dGVycGxvdHMgb2YgdGhlIGZpdmUgbW9zdCBpbXBvcnRhbnQgbnVtZXJpYyBmZWF0dXJlcy4gCgoKVGhlIHBhaXJ3aXNlIHNjYXR0ZXJwbG90cyByZXZlYWwgc2V2ZXJhbCBtb2RlcmF0ZSBsaW5lYXIgcmVsYXRpb25zaGlwcyBhbW9uZyB0aGUgdG9wIGZlYXR1cmVzLiBTcGVjaWZpY2FsbHksIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIChzeXNCUCkgYW5kIGJvZHkgbWFzcyBpbmRleCAoQk1JKSBleGhpYml0IGEgbW9kZXJhdGUgbGluZWFyIHJlbGF0aW9uc2hpcCwgYXMgZG8gc3lzQlAgYW5kIHRvdGFsIGNob2xlc3Rlcm9sICh0b3RDaG9sKSwgQk1JIGFuZCB0b3RDaG9sLCBzeXNCUCBhbmQgYWdlLCBhbmQgdG90Q2hvbCBhbmQgYWdlLiBUaGVzZSByZWxhdGlvbnNoaXBzIHN1Z2dlc3QgdGhhdCBhcyBvbmUgdmFyaWFibGUgaW5jcmVhc2VzLCB0aGVyZSB0ZW5kcyB0byBiZSBhIGNvcnJlc3BvbmRpbmcgaW5jcmVhc2UgaW4gdGhlIG90aGVyLCBpbmRpY2F0aW5nIGEgbW9kZXJhdGUgYXNzb2NpYXRpb24gYmV0d2VlbiB0aGVzZSBrZXkgaGVhbHRoIGZhY3RvcnMuCgoKCgpgYGB7cn0KIyBTZXQgdXAgdGhlIHBsb3R0aW5nIGFyZWEgdG8gaGF2ZSAyIHJvd3MgYW5kIDMgY29sdW1ucwpwYXIobWZyb3cgPSBjKDIsIDMpKQoKIyBMaXN0IG9mIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB0byBjb21wYXJlIHdpdGggVGVuWWVhckNIRApjYXRlZ29yaWNhbF92YXJzIDwtIGMoInNleCIsICJjdXJyZW50U21va2VyIiwgIkJQTWVkcyIsICJwcmV2YWxlbnRTdHJva2UiLCAicHJldmFsZW50SHlwIiwgImRpYWJldGVzIikKCiMgTG9vcCB0aHJvdWdoIGVhY2ggY2F0ZWdvcmljYWwgdmFyaWFibGUKZm9yICh2YXIgaW4gY2F0ZWdvcmljYWxfdmFycykgewogICMgQ3JlYXRlIG1vc2FpYyBwbG90IGZvciBlYWNoIHZhcmlhYmxlIGNvbXBhcmVkIHdpdGggVGVuWWVhckNIRAogIG1vc2FpY3Bsb3QodGFibGUoZmhzW1t2YXJdXSwgZmhzJFRlblllYXJDSEQpLCBtYWluID0gcGFzdGUoIlRlblllYXJDSEQgdnMiLCB2YXIpLCAKICAgICAgICAgICAgIGNvbG9yID0gYygibGlnaHRibHVlIiwgInBpbmsiKSwgCiAgICAgICAgICAgICB4bGFiID0gcGFzdGUodmFyKSwgeWxhYiA9ICJUZW5ZZWFyQ0hEIiwKICAgICAgICAgICAgIGxhcyA9IDEpCiAgIyBBZGQgbGFiZWxzIGZvciAxID0gWWVzLCAwID0gTm8KICB0ZXh0KHggPSAxLCB5ID0gMSwgbGFiZWxzID0gIjEgPSBZZXMiLCBjb2wgPSAiYmxhY2siLCBwb3MgPSA0KQogIHRleHQoeCA9IDEsIHkgPSAyLCBsYWJlbHMgPSAiMCA9IE5vIiwgY29sID0gImJsYWNrIiwgcG9zID0gNCkKfQoKCgpgYGAKCkZpZ3VyZSBKLiBNb3NhaWMgcGxvdHMgb2YgcmVsYXRpb25zaGlwcyBvZiBjYXRlZ29yaWNhbCBmZWF0dXJlcy4gCgoKVGhlIG1vc2FpYyBwbG90cyByZXZlYWwgdGhhdCB0aGVyZSBpcyBhIHNpZ25pZmljYW50IGFzc29jaWF0aW9uIGJldHdlZW4gVGVuWWVhckNIRCBhbmQgYWxsIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMsIGV4Y2VwdCBmb3Ig4oCcY3VycmVudFNtb2tlci7igJ0gU3BlY2lmaWNhbGx5LCB2YXJpYWJsZXMgbGlrZSDigJxzZXgs4oCdIOKAnEJQTWVkcyzigJ0g4oCccHJldmFsZW50U3Ryb2tlLOKAnSDigJxwcmV2YWxlbnRIeXAs4oCdIGFuZCDigJxkaWFiZXRlc+KAnSBzaG93IGNsZWFyIHBhdHRlcm5zIHdoZXJlIHRoZSBkaXN0cmlidXRpb24gb2YgVGVuWWVhckNIRCBkaWZmZXJzIGFjcm9zcyB0aGVpciBsZXZlbHMuIEhvd2V2ZXIsIOKAnGN1cnJlbnRTbW9rZXLigJ0gZG9lcyBub3QgYXBwZWFyIHRvIGV4aGliaXQgYSBzdHJvbmcgYXNzb2NpYXRpb24gd2l0aCB0aGUgb3V0Y29tZSwgc3VnZ2VzdGluZyB0aGF0IHNtb2tpbmcgc3RhdHVzIG1heSBub3QgYmUgYSBzdHJvbmcgcHJlZGljdG9yIG9mIFRlblllYXJDSEQgaW4gdGhpcyBkYXRhc2V0LgoKCgojIEZFQVRVUkUgRU5HSU5FRVIgCgpUbyBlbmhhbmNlIG1vZGVsIHBlcmZvcm1hbmNlIGFuZCB1bmNvdmVyIG1lYW5pbmdmdWwgcGF0dGVybnMsIGZlYXR1cmUgZW5naW5lZXJpbmcgd2FzIGFwcGxpZWQgdG8gdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkgZGF0YXNldC4gVGhpcyBwcm9jZXNzIGluY2x1ZGVkIGNyZWF0aW5nIG5ldyB2YXJpYWJsZXMsIHN1Y2ggYXMgY2x1c3RlcnMsIGludGVyYWN0aW9uIHRlcm1zLCBhbmQgY2xpbmljYWxseSByZWxldmFudCByaXNrIGluZGljYXRvcnMsIHRvIGJldHRlciBjYXB0dXJlIGNvbXBsZXggcmVsYXRpb25zaGlwcyB3aXRoIHRlbi15ZWFyIGNhcmRpb3Zhc2N1bGFyIGRpc2Vhc2Ugcmlzay4KCgoKYGBge3J9CgojIFJlbW92ZSBub24tbnVtZXJpYyBjb2x1bW5zIChsaWtlIGZhY3RvcnMgYW5kIHJlc3BvbnNlIHZhcmlhYmxlKQpmaHNfbnVtZXJpYyA8LSBmaHNbc2FwcGx5KGZocywgaXMubnVtZXJpYyldCmZoc19zY2FsZWQgPC0gc2NhbGUoZmhzX251bWVyaWMpICAjIFN0YW5kYXJkaXplIHRoZSBmZWF0dXJlcwoKc2V0LnNlZWQoMTIzKQoKIyBTdGFuZGFyZGl6ZSBudW1lcmljIHZhcmlhYmxlcwpmaHNfc2NhbGVkIDwtIHNjYWxlKGZoc1ssIHNhcHBseShmaHMsIGlzLm51bWVyaWMpXSkKCiMgQ29tcHV0ZSBXU1MgZm9yIGsgPSAxIHRvIDEwCndzcyA8LSBzYXBwbHkoMToxMCwgZnVuY3Rpb24oayl7CiAga21lYW5zKGZoc19zY2FsZWQsIGNlbnRlcnMgPSBrLCBuc3RhcnQgPSAxMCkkdG90LndpdGhpbnNzCn0pCgojIFBsb3QgRWxib3cgQ3VydmUKcGxvdCgxOjEwLCB3c3MsIHR5cGUgPSAiYiIsIHBjaCA9IDE5LCBmcmFtZSA9IEZBTFNFLAogICAgIHhsYWIgPSAiTnVtYmVyIG9mIGNsdXN0ZXJzIEsiLAogICAgIHlsYWIgPSAiVG90YWwgd2l0aGluLWNsdXN0ZXJzIHN1bSBvZiBzcXVhcmVzIiwKICAgICBtYWluID0gIkVsYm93IE1ldGhvZCBmb3IgRGV0ZXJtaW5pbmcgT3B0aW1hbCBLIikKCgojIFJ1biBrLW1lYW5zIHdpdGggY2hvc2VuIG51bWJlciBvZiBjbHVzdGVycyAoZS5nLiwgMykKa21lYW5zX3Jlc3VsdCA8LSBrbWVhbnMoZmhzX3NjYWxlZCwgY2VudGVycyA9IDMsIG5zdGFydCA9IDI1KQoKIyBBZGQgY2x1c3RlciB0byB0aGUgZGF0YXNldApmaHMkQ2x1c3RlciA8LSBhcy5mYWN0b3Ioa21lYW5zX3Jlc3VsdCRjbHVzdGVyKQoKIyBSdW4gUENBIGp1c3QgZm9yIHBsb3R0aW5nICgyRCByZXByZXNlbnRhdGlvbikKcGNhX3Jlc3VsdCA8LSBwcmNvbXAoZmhzX3NjYWxlZCkKCiMgQ3JlYXRlIGRhdGEgZnJhbWUgZm9yIHBsb3R0aW5nCnBsb3RfZGF0YSA8LSBkYXRhLmZyYW1lKFBDMSA9IHBjYV9yZXN1bHQkeFssMV0sCiAgICAgICAgICAgICAgICAgICAgICAgIFBDMiA9IHBjYV9yZXN1bHQkeFssMl0sCiAgICAgICAgICAgICAgICAgICAgICAgIENsdXN0ZXIgPSBmaHMkQ2x1c3RlcikKCiMgUGxvdCB0aGUgY2x1c3RlcnMKbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3IgPSBDbHVzdGVyKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGFscGhhID0gMC44KSArCiAgbGFicyh0aXRsZSA9ICJLLU1lYW5zIENsdXN0ZXJpbmcgKFZpc3VhbGl6ZWQgdmlhIFBDQSkiLAogICAgICAgeCA9ICJQcmluY2lwYWwgQ29tcG9uZW50IDEiLAogICAgICAgeSA9ICJQcmluY2lwYWwgQ29tcG9uZW50IDIiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikKCgoKYGBgCgpGaWd1cmUgSy4gVmlzdWFsaXphdGlvbiBvZiBjbHVzdGVycy4gCgoKVGhlIGNsdXN0ZXIgYW5hbHlzaXMgcmV2ZWFsZWQgdGhyZWUgd2VsbC1zZXBhcmF0ZWQgYW5kIGRpc3RpbmN0IGdyb3VwcyB3aXRoaW4gdGhlIEZyYW1pbmdoYW0gSGVhcnQgZGF0YXNldCwgc3VnZ2VzdGluZyBtZWFuaW5nZnVsIHVuZGVybHlpbmcgcGF0dGVybnMgaW4gdGhlIGRhdGEuIFRoZXNlIGNsdXN0ZXJzIG1heSByZXByZXNlbnQgZGlmZmVyZW50IGNhcmRpb3Zhc2N1bGFyIHJpc2sgcHJvZmlsZXMsIHdoaWNoIGNvdWxkIGhlbHAgaW5mb3JtIHRhcmdldGVkIHByZXZlbnRpb24gb3IgaW50ZXJ2ZW50aW9uIHN0cmF0ZWdpZXMuCgoKCiMgTUVUSE9ET0xPR1k6IFByZWRpY3RpdmUgTW9kZWxpbmcgQXBwcm9hY2hlcwoKUHJldmlvdXMgbWV0aG9kcywgc3VjaCBhcyByZWdyZXNzaW9uIG1vZGVscyAoaW5jbHVkaW5nIHJlZ3VsYXJpemVkIHZlcnNpb25zIGxpa2UgTGFzc28gYW5kIFJpZGdlKSBhbmQgU1ZNcywgd2VyZSB1c2VkIHRvIGFkZHJlc3MgdGhlIGZvcm11bGF0ZWQgcXVlc3Rpb25zLCBleGNlbGxpbmcgaW4gbGluZWFyIHJlbGF0aW9uc2hpcHMgYW5kIHNtYWxsZXIgZGF0YXNldHMuIEhvd2V2ZXIsIHRoZXNlIG1vZGVscyBjYW4gc3RydWdnbGUgd2l0aCBub24tbGluZWFyIGRhdGEgb3IgaGlnaC1kaW1lbnNpb25hbCBmZWF0dXJlIHNwYWNlcy4KCkNBUlQsIEJhZ2dpbmcsIGFuZCBSYW5kb20gRm9yZXN0cyBvZmZlciBhZHZhbnRhZ2VzIGJ5IGNhcHR1cmluZyBjb21wbGV4LCBub24tbGluZWFyIHJlbGF0aW9uc2hpcHMgd2l0aG91dCB0aGUgbmVlZCBmb3IgbGluZWFyIGFzc3VtcHRpb25zLiBSYW5kb20gRm9yZXN0cywgaW4gcGFydGljdWxhciwgaW1wcm92ZSB1cG9uIEJhZ2dpbmcgYnkgY29uc3RydWN0aW5nIG11bHRpcGxlIGRlY2lzaW9uIHRyZWVzIGFuZCBhdmVyYWdpbmcgdGhlaXIgb3V0cHV0cywgd2hpY2ggaGVscHMgdG8gZnVydGhlciByZWR1Y2UgdmFyaWFuY2UgYW5kIGF2b2lkIG92ZXJmaXR0aW5nLiBXaGlsZSBDQVJUIGlzIGludGVycHJldGFibGUsIGl0IG1heSBvdmVyZml0IHdpdGhvdXQgcHJvcGVyIHBydW5pbmcuIEJhZ2dpbmcgaGVscHMgcmVkdWNlIHZhcmlhbmNlIGJ5IGNvbWJpbmluZyBtdWx0aXBsZSBtb2RlbHMsIGFuZCBSYW5kb20gRm9yZXN0cyBmdXJ0aGVyIGltcHJvdmUgcGVyZm9ybWFuY2Ugd2l0aCBlbnNlbWJsZSBsZWFybmluZy4gRWFjaCBtZXRob2QgaGFzIGl0cyBzdHJlbmd0aHM6IHJlZ3Jlc3Npb24gbW9kZWxzIGFuZCBTVk1zIGV4Y2VsIGF0IGhhbmRsaW5nIHNtYWxsZXIsIHNpbXBsZXIgZGF0YXNldHMgd2l0aCBsaW5lYXIgb3IgcmVndWxhcml6ZWQgcmVsYXRpb25zaGlwcywgd2hpbGUgQ0FSVCwgQmFnZ2luZywgYW5kIFJhbmRvbSBGb3Jlc3RzIHNoaW5lIHdpdGggbGFyZ2VyIGRhdGFzZXRzIGFuZCBtb3JlIGNvbXBsZXggaW50ZXJhY3Rpb25zLiBUaGUgdHJhZGUtb2ZmIGJldHdlZW4gaW50ZXJwcmV0YWJpbGl0eSBhbmQgcGVyZm9ybWFuY2UgaXMgYSBrZXkgY29uc2lkZXJhdGlvbiB3aGVuIHNlbGVjdGluZyB0aGUgYXBwcm9wcmlhdGUgbW9kZWwgZm9yIGEgZ2l2ZW4gdGFzay4KCgojIENBUlQgUkVHUkVTU0lPTgoKQ0FSVCAoQ2xhc3NpZmljYXRpb24gYW5kIFJlZ3Jlc3Npb24gVHJlZXMpIHJlZ3Jlc3Npb24gaXMgYSBkZWNpc2lvbiB0cmVlIGFsZ29yaXRobSB1c2VkIHRvIHByZWRpY3QgY29udGludW91cyBvdXRjb21lcywgc3VjaCBhcyB0b3RhbCBjaG9sZXN0ZXJvbCAodG90Q2hvbCkgaW4gdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkgZGF0YXNldC4gVGhlIG1vZGVsIGRldmVsb3BtZW50IGZvbGxvd3MgdGhlc2Uga2V5IGNvbXBvbmVudHM6CgoJMS4JU3BsaXR0aW5nIENyaXRlcmlvbjogVGhlIHRyZWUgcmVjdXJzaXZlbHkgcGFydGl0aW9ucyB0aGUgZGF0YSBieSBjaG9vc2luZyBzcGxpdHMgdGhhdCBtaW5pbWl6ZSB0aGUgc3VtIG9mIHNxdWFyZWQgZXJyb3JzIChTU0UpIHdpdGhpbiB0aGUgcmVzdWx0aW5nIG5vZGVzLCBhaW1pbmcgdG8gY3JlYXRlIGhvbW9nZW5vdXMgZ3JvdXBzIGJhc2VkIG9uIHByZWRpY3RvcnMgbGlrZSBhZ2UsIEJNSSwgYW5kIGJsb29kIHByZXNzdXJlLgoJMi4JVHJlZSBTdHJ1Y3R1cmUgYW5kIFN0b3BwaW5nIFJ1bGVzOiBUbyBwcmV2ZW50IHRoZSB0cmVlIGZyb20gZ3Jvd2luZyB0b28gY29tcGxleCwgc3RvcHBpbmcgcnVsZXMgc3VjaCBhcyBtaW5pbXVtIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgcmVxdWlyZWQgdG8gc3BsaXQgYSBub2RlIChtaW5zcGxpdCksIG1pbmltdW0gc2l6ZSBvZiB0ZXJtaW5hbCBub2RlcyAobWluYnVja2V0KSwgYW5kIG1heGltdW0gdHJlZSBkZXB0aCAobWF4ZGVwdGgpIGFyZSBhcHBsaWVkLgoJMy4JUHJ1bmluZzogQWZ0ZXIgdGhlIGZ1bGwgdHJlZSBpcyBncm93biwgY29zdC1jb21wbGV4aXR5IHBydW5pbmcgaXMgdXNlZCB0byBhdm9pZCBvdmVyZml0dGluZy4gVGhpcyBpbnZvbHZlcyBldmFsdWF0aW5nIHN1YnRyZWVzIHVzaW5nIGNyb3NzLXZhbGlkYXRpb24gYW5kIHNlbGVjdGluZyB0aGUgb3B0aW1hbCBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIHRoYXQgYmFsYW5jZXMgbW9kZWwgYWNjdXJhY3kgd2l0aCBzaW1wbGljaXR5LgoJNC4JSW50ZXJwcmV0YWJpbGl0eTogVGhlIGZpbmFsIHRyZWUgc3RydWN0dXJlIHByb3ZpZGVzIGEgdHJhbnNwYXJlbnQgbW9kZWwgc2hvd2luZyBob3cgZGlmZmVyZW50IGNvbWJpbmF0aW9ucyBvZiBwcmVkaWN0b3JzIGxlYWQgdG8gZGlmZmVyZW50IHByZWRpY3RlZCBjaG9sZXN0ZXJvbCBsZXZlbHMuCgojIyBJbml0aWFsIFJlZ3Jlc3Npb24gVHJlZQoKQW4gaW5pdGlhbCByZWdyZXNzaW9uIHRyZWUgd2FzIGJ1aWx0IHRvIGV4cGxvcmUgaG93IHZhcmlvdXMgaGVhbHRoIGFuZCBsaWZlc3R5bGUgZmFjdG9ycyBwcmVkaWN0IHRvdGFsIGNob2xlc3Rlcm9sIGxldmVscyBpbiB0aGUgRnJhbWluZ2hhbSBkYXRhc2V0LiBCeSBzZXR0aW5nIHJlbGF0aXZlbHkgZmxleGlibGUgdHJlZSBwYXJhbWV0ZXJzLCB0aGUgbW9kZWwgd2FzIGFibGUgdG8gaWRlbnRpZnkga2V5IHZhcmlhYmxlcyBhbmQgcG90ZW50aWFsIG5vbmxpbmVhciByZWxhdGlvbnNoaXBzIHRocm91Z2ggcmVjdXJzaXZlIHBhcnRpdGlvbmluZy4KCmBgYHtyfQoKIyBTZXQgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CnNldC5zZWVkKDEyMykKCgojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgKDcwJSkgYW5kIHRlc3RpbmcgKDMwJSkgc2V0cwp0cmFpbkluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oZmhzJFRlblllYXJDSEQsIHAgPSAwLjcsIGxpc3QgPSBGQUxTRSkKdHJhaW5fZGF0YSA8LSBmaHNbdHJhaW5JbmRleCwgXQp0ZXN0LmRhdGEgPC0gZmhzWy10cmFpbkluZGV4LCBdCgoKIyBCdWlsZCB0aGUgaW5pdGlhbCBDQVJUIHJlZ3Jlc3Npb24gdHJlZSBmb3IgcHJlZGljdGluZyB0b3RhbCBjaG9sZXN0ZXJvbAp0cmVlX21vZGVsIDwtIHJwYXJ0KAogIHRvdENob2wgfiAuLCAKICBkYXRhID0gdHJhaW5fZGF0YSwKICBtZXRob2QgPSAiYW5vdmEiLCAgIyBSZWdyZXNzaW9uIHRyZWUKICBjb250cm9sID0gcnBhcnQuY29udHJvbCgKICAgIG1pbnNwbGl0ID0gMTAsICAgICAgICAgIyBNaW5pbXVtIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgcmVxdWlyZWQgdG8gYXR0ZW1wdCBhIHNwbGl0CiAgICBtaW5idWNrZXQgPSA1LCAgICAgICAgICMgTWluaW11bSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRlcm1pbmFsIG5vZGVzCiAgICBjcCA9IDAuMDAyLCAgICAgICAgICAgICAjIENvbXBsZXhpdHkgcGFyYW1ldGVyIAogICAgbWF4ZGVwdGggPSA1ICAgICAgICAgICAjIE1heGltdW0gZGVwdGggb2YgdGhlIHRyZWUKICApCikKCiMgVmlzdWFsaXplIHRoZSB1bnBydW5lZCByZWdyZXNzaW9uIHRyZWUKcnBhcnQucGxvdCh0cmVlX21vZGVsLCBtYWluID0gIkluaXRpYWwgQ0FSVCBSZWdyZXNzaW9uIFRyZWUgZm9yIFRvdGFsIENob2xlc3Rlcm9sIikKCgpgYGAKCkZpZ3VyZSBMLiBJbml0aWFsIHJlZ3Jlc3Npb24gdHJlZSBiZWZvcmUgcHJ1bmluZy4gCgoKIyBIeXBlcnBhcmFtZXRlciBUdW5pbmcKClRvIGltcHJvdmUgbW9kZWwgcGVyZm9ybWFuY2UgYW5kIHByZXZlbnQgb3ZlcmZpdHRpbmcsIHdlIHBlcmZvcm1lZCBoeXBlcnBhcmFtZXRlciB0dW5pbmcgb24gdGhlIENBUlQgcmVncmVzc2lvbiBtb2RlbCB1c2luZyAxMC1mb2xkIGNyb3NzLXZhbGlkYXRpb24uIFNwZWNpZmljYWxseSwgd2UgdHVuZWQgdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCksIHdoaWNoIGNvbnRyb2xzIHRoZSB0cmFkZS1vZmYgYmV0d2VlbiB0cmVlIHNpemUgYW5kIHByZWRpY3RpdmUgYWNjdXJhY3kuCgoKYGBge3J9CiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgxMjMpCgojIEJ1aWxkIHRoZSBpbml0aWFsIHJlZ3Jlc3Npb24gdHJlZSB3aXRoIGNyb3NzLXZhbGlkYXRpb24KdHJlZV9tb2RlbCA8LSBycGFydCgKICB0b3RDaG9sIH4gLiwgCiAgZGF0YSA9IHRyYWluX2RhdGEsCiAgbWV0aG9kID0gImFub3ZhIiwgICMgUmVncmVzc2lvbiB0cmVlCiAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woCiAgICBtaW5zcGxpdCA9IDEwLAogICAgbWluYnVja2V0ID0gNSwKICAgIGNwID0gMC4wMSwKICAgIG1heGRlcHRoID0gNSwKICAgIHh2YWwgPSAxMAogICkKKQoKIyBWaWV3IHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciB0YWJsZQpwYW5kZXIodHJlZV9tb2RlbCRjcHRhYmxlKQoKIyBQbG90IHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciB2cy4gZXJyb3IgKHhlcnJvcikKcGxvdGNwKHRyZWVfbW9kZWwpCgoKIyBDaG9vc2UgdGhlIG9wdGltYWwgY3AgYmFzZWQgb24geGVycm9yCm1pbl94ZXJyb3IgPC0gbWluKHRyZWVfbW9kZWwkY3B0YWJsZVssICJ4ZXJyb3IiXSkKYmVzdF9jcF9yb3cgPC0gd2hpY2gubWluKHRyZWVfbW9kZWwkY3B0YWJsZVssICJ4ZXJyb3IiXSkKYmVzdF9jcCA8LSB0cmVlX21vZGVsJGNwdGFibGVbYmVzdF9jcF9yb3csICJDUCJdCgojIFByaW50IHRoZSBiZXN0IGNwIHZhbHVlCmNhdCgiQmVzdCBDUDogIiwgcm91bmQoYmVzdF9jcCwgNCksICJcbiIpCgoKYGBgClRhYmxlIDQuIENyb3NzLXZhbGlkYXRpb24gcmVzdWx0cyBmb3IgZGlmZmVyZW50IGNvbXBsZXhpdHkgcGFyYW1ldGVycy4gCgpGaWd1cmUgTS4gUGxvdCBpbGx1c3RyYXRlcyB0aGUgZXJyb3IgY3VydmUuCgpBbHRob3VnaCB0aGUgY29tcGxleGl0eSBwbG90IHNob3dzIGFuIGVsYm93IGF0IGNwID0gMC4wNTEsIGluZGljYXRpbmcgYSBwb2ludCB3aGVyZSBmdXJ0aGVyIHNwbGl0cyBiZWdpbiB0byB5aWVsZCBkaW1pbmlzaGluZyByZXR1cm5zLCBjcm9zcy12YWxpZGF0aW9uIGlkZW50aWZpZWQgY3AgPSAwLjAxIGFzIHRoZSBvcHRpbWFsIHZhbHVlIGZvciBtaW5pbWl6aW5nIHByZWRpY3Rpb24gZXJyb3Igd2hpbGUgbWFpbnRhaW5pbmcgYSByZWFzb25hYmxlIG1vZGVsIGNvbXBsZXhpdHkuIE1vcmUgc3BlY2lmaWNhbGx5LCB0aGUgcGxvdCBpbmRpY2F0ZXMgdGhhdCBjcCA9IDAuMTIgaXMgYmVzdCB3aGVuIHVzaW5nIHRoZSAxLXN0YW5kYXJkIGVycm9yICgxLVNFKSBydWxlLgoKCiMjIFBydW5lZCBSZWdyZXNzaW9uIFRyZWU6IEJlc3QgQ1AgKDEtU0UpIAoKClRoaXMgY29kZSBzZWxlY3RzIGNvbXBsZXhpdHkgcGFyYW1ldGVycyAoY3ApIGZvciBwcnVuaW5nIHRoZSByZWdyZXNzaW9uIHRyZWUgYmFzZWQgb24gYm90aCB0aGUgbWluaW11bSBjcm9zcy12YWxpZGF0aW9uIGVycm9yIGFuZCB0aGUgMS1TRSBydWxlLiBJdCB0aGVuIHBydW5lcyB0aGUgdHJlZSBhbmQgbWFrZXMgcHJlZGljdGlvbnMgb24gdGhlIHRlc3Qgc2V0LCBzYXZpbmcgdGhlIFItc3F1YXJlZCBhbmQgUk1TRSB2YWx1ZXMgZm9yIGxhdGVyIGNvbXBhcmlzb24gYWNyb3NzIHJlZ3Jlc3Npb24gbW9kZWxzLgoKYGBge3J9CiMgU3RlcCAxOiBJZGVudGlmeSB0aGUgbWluaW11bSB4ZXJyb3IgYW5kIGl0cyBjb3JyZXNwb25kaW5nIGNwIChmb3IgcHJ1bmluZyB3aXRoIG1pbmltdW0gZXJyb3IpCmNwLnRhYmxlIDwtIHRyZWVfbW9kZWwkY3B0YWJsZQptaW4ueGVycm9yIDwtIG1pbihjcC50YWJsZVssICJ4ZXJyb3IiXSkKbWluLmNwLnJvdyA8LSB3aGljaC5taW4oY3AudGFibGVbLCAieGVycm9yIl0pCm1pbi5jcCA8LSBjcC50YWJsZVttaW4uY3Aucm93LCAiQ1AiXQoKIyBTdGVwIDI6IENvbXB1dGUgdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBtaW5pbXVtIHhlcnJvcgp4ZXJyb3Iuc3RkIDwtIGNwLnRhYmxlW21pbi5jcC5yb3csICJ4c3RkIl0KdGhyZXNob2xkIDwtIG1pbi54ZXJyb3IgKyB4ZXJyb3Iuc3RkCgojIFN0ZXAgMzogT3ZlcnJpZGUgYmVzdC5jcCBtYW51YWxseSB0byBnZXQgYSBtb3JlIGNvbXBsZXggdHJlZQpiZXN0LmNwIDwtIDAuMDEyICAjIGJhc2VkIG9uIDEtU0UgcnVsZSwgbWF4IENQIGZvciB4ZXJyb3IgPCAxCgojIFN0ZXAgNDogUHJ1bmUgdGhlIHRyZWUgdXNpbmcgYm90aCBtaW4uY3AgYW5kIG1hbnVhbGx5IHNwZWNpZmllZCBiZXN0LmNwCnBydW5lZC50cmVlLmJlc3QuY3AgPC0gcHJ1bmUodHJlZV9tb2RlbCwgY3AgPSBiZXN0LmNwKQpwcnVuZWQudHJlZS5taW4uY3AgPC0gcHJ1bmUodHJlZV9tb2RlbCwgY3AgPSBtaW4uY3ApCgojIFN0ZXAgNTogVmlzdWFsaXplIHRoZSBtYW51YWxseSBwcnVuZWQgdHJlZQpycGFydC5wbG90KHBydW5lZC50cmVlLmJlc3QuY3AsIAogICAgICAgICAgIG1haW4gPSBwYXN0ZSgiUHJ1bmVkIFRyZWUgQmVzdCBDUCAoMS1TRSk6IGNwID0iLCByb3VuZChiZXN0LmNwLCA1KSkpCgoKCiMgU3RlcCA2OiBNYWtlIHByZWRpY3Rpb25zIG9uIHRlc3QgZGF0YQpwcmVkLmJlc3QuY3AgPC0gcHJlZGljdChwcnVuZWQudHJlZS5iZXN0LmNwLCBuZXdkYXRhID0gdGVzdC5kYXRhKQpwcmVkLm1pbi5jcCAgPC0gcHJlZGljdChwcnVuZWQudHJlZS5taW4uY3AsIG5ld2RhdGEgPSB0ZXN0LmRhdGEpCgojIFN0ZXAgNzogU2F2ZSBSLXNxdWFyZWQgdmFsdWVzCnJzcXVhcmVkLmJlc3RjcCA8LSBjb3IodGVzdC5kYXRhJHRvdENob2wsIHByZWQuYmVzdC5jcCleMgoKIyBTYXZlIE1TRSBmb3IgYmVzdC5jcCAoMS1TRSBydWxlKQpybXNlLmJlc3RjcCA8LSBzcXJ0KG1lYW4oKHByZWQuYmVzdC5jcCAtIHRlc3QuZGF0YSR0b3RDaG9sKV4yKSkKCgoKYGBgCgpGaWd1cmUgTi4gUHJ1bmVkIFRyZWUgQmVzdCBDUCB1c2luZyAxLVNFIFJ1bGUuIAoKCiAKCgoKIyMgUHJ1bmVkIFJlZ3Jlc3Npb24gVHJlZTogTWluaW11bSBDUAoKVGhlIGZvbGxvd2luZyB0cmVlIHJlcHJlc2VudHMgdGhlIHBydW5lZCByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciAoQ1ApIHRoYXQgbWluaW1pemVzIGNyb3NzLXZhbGlkYXRpb24gZXJyb3IuIFRoaXMgdmVyc2lvbiBvZiB0aGUgdHJlZSAoTWluaW11bSBDUCkgYWltcyB0byBiYWxhbmNlIG1vZGVsIGNvbXBsZXhpdHkgYW5kIHByZWRpY3RpdmUgYWNjdXJhY3kgYnkgZml0dGluZyBhcyBjbG9zZWx5IGFzIHBvc3NpYmxlIHRvIHRoZSB0cmFpbmluZyBkYXRhIHdpdGhvdXQgb3ZlcmZpdHRpbmcuCgoKCmBgYHtyfQojIFZpc3VhbGl6ZSB0aGUgcHJ1bmVkIHRyZWUgdXNpbmcgdGhlIG1pbmltdW0gQ1AgdmFsdWUKcnBhcnQucGxvdChwcnVuZWQudHJlZS5taW4uY3AsCiAgICAgICAgICAgbWFpbiA9IHBhc3RlKCJQcnVuZWQgUmVncmVzc2lvbiBUcmVlIChNaW4gQ1ApOiBjcCA9Iiwgcm91bmQobWluLmNwLCA0KSksCiAgICAgICAgICAgdHlwZSA9IDIsICAgICAgICAgICAgICAgICMgTGFiZWwgYWxsIG5vZGVzIHdpdGggcHJlZGljdGVkIHZhbHVlcwogICAgICAgICAgIGV4dHJhID0gMTAxLCAgICAgICAgICAgICAjIFNob3cgZml0dGVkIHZhbHVlcyBhbmQgJSBvZiBvYnNlcnZhdGlvbnMgYXQgZWFjaCBub2RlCiAgICAgICAgICAgdW5kZXIgPSBUUlVFLCAgICAgICAgICAgICMgRGlzcGxheSB0aGUgc3BsaXQgY29uZGl0aW9uIHVuZGVybmVhdGggdGhlIG5vZGUKICAgICAgICAgICBmYWNsZW4gPSAwLCAgICAgICAgICAgICAgIyBBdm9pZCBhYmJyZXZpYXRpbmcgZmFjdG9yIGxldmVscwogICAgICAgICAgIGNleCA9IDAuNywgICAgICAgICAgICAgICAjIFNocmluayB0ZXh0IGZvciBiZXR0ZXIgZml0CiAgICAgICAgICAgZmFsbGVuLmxlYXZlcyA9IFRSVUUsICAgICMgQmV0dGVyIGxheW91dCBmb3IgdGhlIGxlYXZlcyBhdCB0aGUgYm90dG9tCiAgICAgICAgICAgYm94LnBhbGV0dGUgPSAiR25CdSIsICAgICMgR3JlZW4tYmx1ZSBjb2xvciB0aGVtZSBmb3IgYm94ZXMKICAgICAgICAgICBzaGFkb3cuY29sID0gImdyYXkiKSAgICAgIyBBZGQgc2hhZG93cyB0byBib3hlcyBmb3IgZGVwdGgKCiNyXjIgZm9yIG1pbi5jcApyc3F1YXJlZC5taW5jcCAgPC0gY29yKHRlc3QuZGF0YSR0b3RDaG9sLCBwcmVkLm1pbi5jcCleMgojU2F2ZSBNU0UgZm9yIG1pbi5jcCAobWluaW11bSB4ZXJyb3IpCnJtc2UubWluY3AgPC0gc3FydChtZWFuKChwcmVkLm1pbi5jcCAtIHRlc3QuZGF0YSR0b3RDaG9sKV4yKSkKYGBgCkZpZ3VyZSBPLiBQcnVuZWQgUmVncmVzc2lvbiBUcmVlLCBNaW5pbXVtIENQLgoKVGhlIGZpbmFsIENBUlQgcmVncmVzc2lvbiBtb2RlbCBwcmVkaWN0aW5nIHRvdGFsIGNob2xlc3Rlcm9sIGlkZW50aWZpZWQgc2V4IGFuZCBjaWdhcmV0dGVzIHBlciBkYXkgYXMgdGhlIG1vc3QgaW1wb3J0YW50IHByZWRpY3RvcnMuIFRoaXMgc3VnZ2VzdHMgdGhhdCBjaG9sZXN0ZXJvbCBsZXZlbHMgaW4gdGhlIEZyYW1pbmdoYW0gZGF0YXNldCBhcmUgbW9zdCBlZmZlY3RpdmVseSBzZWdtZW50ZWQgYnkgZ2VuZGVyIGFuZCBzbW9raW5nIGJlaGF2aW9yLgoKCiMjIEJBR0dJTkcgQ0FSVCBSZWdyZXNzaW9uCgpUbyBmdXJ0aGVyIGltcHJvdmUgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSwgd2UgbmV4dCBhcHBseSBCYWdnaW5nIChCb290c3RyYXAgQWdncmVnYXRpbmcpIHRvIENBUlQgcmVncmVzc2lvbi4gQmFnZ2luZyBidWlsZHMgbXVsdGlwbGUgdHJlZXMgb24gZGlmZmVyZW50IGJvb3RzdHJhcCBzYW1wbGVzIG9mIHRoZSBkYXRhIGFuZCBhdmVyYWdlcyB0aGVpciBwcmVkaWN0aW9ucywgaGVscGluZyB0byByZWR1Y2UgdmFyaWFuY2UgYW5kIGltcHJvdmUgbW9kZWwgc3RhYmlsaXR5IGNvbXBhcmVkIHRvIGEgc2luZ2xlIGRlY2lzaW9uIHRyZWUuCgpUbyBvcHRpbWl6ZSB0aGUgQmFnZ2luZyBDQVJUIHJlZ3Jlc3Npb24gbW9kZWwsIHdlIHBlcmZvcm0gYSBzeXN0ZW1hdGljIGh5cGVycGFyYW1ldGVyIHR1bmluZyBwcm9jZXNzLiBXZSBzcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMsIHNldCB1cCA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiwgYW5kIGV4cGxvcmUgZGlmZmVyZW50IGNvbWJpbmF0aW9ucyBvZiB0aGUgbnVtYmVyIG9mIGJhZ2dlZCB0cmVlcywgY29tcGxleGl0eSBwYXJhbWV0ZXJzIChjcCksIGFuZCBtYXhpbXVtIHRyZWUgZGVwdGhzIHRvIGlkZW50aWZ5IHRoZSBzZXR0aW5ncyB0aGF0IG1pbmltaXplIG91dC1vZi1iYWcgKE9PQikgZXJyb3IuCgoKYGBge3J9CgojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cwpzZXQuc2VlZCgxMjMpCnRyYWluLmluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oZmhzJHRvdENob2wsIHAgPSAwLjgsIGxpc3QgPSBGQUxTRSkKdHJhaW4uZGF0YSA8LSBmaHNbdHJhaW4uaW5kZXgsIF0KdGVzdC5kYXRhIDwtIGZoc1stdHJhaW4uaW5kZXgsIF0KCiMgU2V0IHVwIHRyYWluIGNvbnRyb2wgZm9yIGNyb3NzLXZhbGlkYXRpb24KY3RybCA8LSB0cmFpbkNvbnRyb2woCiAgbWV0aG9kID0gImN2IiwKICBudW1iZXIgPSA1LAogIHZlcmJvc2VJdGVyID0gVFJVRQopCgojIERlZmluZSBwYXJhbWV0ZXIgY29tYmluYXRpb25zIHRvIHRlc3QKbmJhZ2cudmFsdWVzIDwtIGMoMTAsIDI1LCA1MCkgICAgICMgbnVtYmVyIG9mIGJhZ2dlZCB0cmVlcwpjcC52YWx1ZXMgPC0gYygwLjAxLCAwLjA1LCAwLjEpICAgIyBjYW5kaWRhdGUgY3AgdmFsdWVzIGZvciBycGFydAptYXhkZXB0aC52YWx1ZXMgPC0gYyg1LCAxMCwgMjApICAgIyBtYXhpbXVtIGRlcHRoIG9mIHRoZSBjYW5kaWRhdGUgdHJlZQoKIyBDcmVhdGUgYW4gZW1wdHkgZGF0YSBmcmFtZSB0byBzdG9yZSByZXN1bHRzCnJlc3VsdHMgPC0gZGF0YS5mcmFtZSgpCgojIE1vZGVsIHR1bmluZyBsb29wCmZvciAobmJhZ2cgaW4gbmJhZ2cudmFsdWVzKSB7CiAgZm9yIChjcCBpbiBjcC52YWx1ZXMpIHsKICAgIGZvciAobWF4ZGVwdGggaW4gbWF4ZGVwdGgudmFsdWVzKSB7CiAgICAgIHNldC5zZWVkKDEyMykKICAgICAgbW9kZWwgPC0gYmFnZ2luZygKICAgICAgICB0b3RDaG9sIH4gLiwKICAgICAgICBkYXRhID0gdHJhaW4uZGF0YSwKICAgICAgICBuYmFnZyA9IG5iYWdnLAogICAgICAgIGNvb2IgPSBUUlVFLAogICAgICAgIHRyQ29udHJvbCA9IGN0cmwsCiAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woY3AgPSBjcCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4ZGVwdGggPSBtYXhkZXB0aCkKICAgICAgKQogICAgICAjIEdldCBPT0IgZXJyb3IgZnJvbSBlYWNoIGl0ZXJhdGlvbgogICAgICBvb2IuZXJyb3IgPC0gbW9kZWwkZXJyCiAgICAgICMgU3RvcmUgcmVzdWx0cwogICAgICByZXN1bHRzIDwtIHJiaW5kKHJlc3VsdHMsIAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUobmJhZ2cgPSBuYmFnZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNwID0gY3AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhkZXB0aCA9IG1heGRlcHRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb29iLmVycm9yID0gb29iLmVycm9yKSkKICAgIH0KICB9Cn0KCiMgRmluZCB0aGUgYmVzdCBjb21iaW5hdGlvbiB0aGF0IHlpZWxkcyB0aGUgbWluaW11bSBvdXQtb2YtYmFnIGVycm9yCmJlc3QucGFyYW1zIDwtIHJlc3VsdHNbd2hpY2gubWluKHJlc3VsdHMkb29iLmVycm9yKSwgXQpwYW5kZXIoYmVzdC5wYXJhbXMpCgoKCmBgYApUYWJsZSA1LiBCZXN0IGh5cGVycGFyYW1ldGVyIGNvbWJpbmF0aW9uIGZvciBCYWdnaW5nIENBUlQgcmVncmVzc2lvbiwgc2hvd2luZyA1MCB0cmVlcywgYSBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIG9mIDAuMDEsIGFuZCBhIG1heGltdW0gdHJlZSBkZXB0aCBvZiA1LCByZXN1bHRpbmcgaW4gdGhlIGxvd2VzdCBvdXQtb2YtYmFnIGVycm9yICgzOS4wOSkuCgoKCgoKVGhlIGZpbmFsIEJhZ2dpbmcgQ0FSVCBtb2RlbCBpcyBmaXR0ZWQgdG8gdGhlIHRyYWluaW5nIGRhdGEgdXNpbmcgdGhlIG9wdGltYWwgaHlwZXJwYXJhbWV0ZXJzIGZyb20gdGhlIHR1bmluZyBwcm9jZXNzLiBQcmVkaWN0aW9ucyBhcmUgdGhlbiBnZW5lcmF0ZWQgb24gdGhlIHRlc3Qgc2V0IGZvciBsYXRlciBldmFsdWF0aW9uLgoKYGBge3J9CgojIFJldHJhaW4gdGhlIGZpbmFsIG1vZGVsIHVzaW5nIHRoZSBiZXN0IHBhcmFtZXRlcnMKc2V0LnNlZWQoMTIzKQpmaW5hbC5iYWdnaW5nLm1vZGVsIDwtIGJhZ2dpbmcoCiAgdG90Q2hvbCB+IC4sCiAgZGF0YSA9IHRyYWluLmRhdGEsCiAgbmJhZ2cgPSBiZXN0LnBhcmFtcyRuYmFnZywKICBjb29iID0gVFJVRSwKICBjb250cm9sID0gcnBhcnQuY29udHJvbChjcCA9IGJlc3QucGFyYW1zJGNwLCBtYXhkZXB0aCA9IGJlc3QucGFyYW1zJG1heGRlcHRoKQopCgojIE1ha2UgcHJlZGljdGlvbnMgb24gdGVzdCBzZXQKcHJlZC5iYWdnaW5nIDwtIHByZWRpY3QoZmluYWwuYmFnZ2luZy5tb2RlbCwgbmV3ZGF0YSA9IHRlc3QuZGF0YSkKCiMgQ2FsY3VsYXRlIE1TRSBhbmQgUi1zcXVhcmVkCnJtc2UuYmFnZ2luZyA8LSBzcXJ0KG1lYW4oKHByZWQuYmFnZ2luZyAtIHRlc3QuZGF0YSR0b3RDaG9sKV4yKSkKCnNzLnJlcyA8LSBzdW0oKHByZWQuYmFnZ2luZyAtIHRlc3QuZGF0YSR0b3RDaG9sKV4yKQpzcy50b3QgPC0gc3VtKChtZWFuKHRyYWluLmRhdGEkdG90Q2hvbCkgLSB0ZXN0LmRhdGEkdG90Q2hvbCleMikKcnNxLmJhZ2dpbmcgPC0gMSAtIChzcy5yZXMgLyBzcy50b3QpCgpgYGAKIAogCkluIHRoZSBuZXh0IHN0ZXAsIHRoZSBpbXBvcnRhbmNlIG9mIGVhY2ggcHJlZGljdG9yIGluIHRoZSBmaW5hbCBCYWdnaW5nIG1vZGVsIGlzIGV2YWx1YXRlZC4gVGhpcyBhbmFseXNpcyBoZWxwcyBpZGVudGlmeSB0aGUgZmVhdHVyZXMgdGhhdCBoYXZlIHRoZSBtb3N0IHNpZ25pZmljYW50IGluZmx1ZW5jZSBvbiBwcmVkaWN0aW5nIHRoZSB0YXJnZXQgdmFyaWFibGUsIG9mZmVyaW5nIGluc2lnaHRzIGludG8gdGhlIG1vZGVs4oCZcyBkZWNpc2lvbi1tYWtpbmcgcHJvY2Vzcy4gCiAKIApgYGB7cn0KICMgRXh0cmFjdCB2YXJpYWJsZSBpbXBvcnRhbmNlIHVzaW5nIGNhcmV0J3MgdmFySW1wIGZ1bmN0aW9uCnZhci5pbXAgPC0gdmFySW1wKGZpbmFsLmJhZ2dpbmcubW9kZWwpCgojIEN1c3RvbSBmdW5jdGlvbiB0byBnZXQgdmFyaWFibGUgaW1wb3J0YW5jZSBmcm9tIGJhZ2dpbmcgbW9kZWwKZ2V0LmJhZ2dpbmcuaW1wb3J0YW5jZSA8LSBmdW5jdGlvbihtb2RlbCkgewogICMgR2V0IGFsbCB0aGUgdHJlZXMgZnJvbSB0aGUgYmFnZ2luZyBtb2RlbAogIHRyZWVzIDwtIG1vZGVsJG10cmVlcwogIAogICMgSW5pdGlhbGl6ZSBpbXBvcnRhbmNlIHZlY3RvcgogIGltcCA8LSBudW1lcmljKGxlbmd0aCh0cmVlc1tbMV1dJGJ0cmVlJHZhcmlhYmxlLmltcG9ydGFuY2UpKQogIG5hbWVzKGltcCkgPC0gbmFtZXModHJlZXNbWzFdXSRidHJlZSR2YXJpYWJsZS5pbXBvcnRhbmNlKQogIAogICMgU3VtIGltcG9ydGFuY2UgYWNyb3NzIGFsbCB0cmVlcwogIGZvciAodHJlZSBpbiB0cmVlcykgewogICAgaW1wW25hbWVzKHRyZWUkYnRyZWUkdmFyaWFibGUuaW1wb3J0YW5jZSldIDwtIAogICAgICBpbXBbbmFtZXModHJlZSRidHJlZSR2YXJpYWJsZS5pbXBvcnRhbmNlKV0gKyAKICAgICAgdHJlZSRidHJlZSR2YXJpYWJsZS5pbXBvcnRhbmNlCiAgfQogIAogICMgQXZlcmFnZSBpbXBvcnRhbmNlCiAgaW1wIDwtIGltcCAvIGxlbmd0aCh0cmVlcykKICByZXR1cm4oaW1wKQp9CgojIEdldCB2YXJpYWJsZSBpbXBvcnRhbmNlIGZyb20gdGhlIGZpbmFsIGJhZ2dpbmcgbW9kZWwKYmFnZ2luZ19pbXBvcnRhbmNlIDwtIGdldC5iYWdnaW5nLmltcG9ydGFuY2UoZmluYWwuYmFnZ2luZy5tb2RlbCkKCiMgRGlzcGxheSB2YXJpYWJsZSBpbXBvcnRhbmNlCiNwcmludChiYWdnaW5nX2ltcG9ydGFuY2UpCgojIEdldCBpbXBvcnRhbmNlCmltcG9ydGFuY2Uuc2NvcmVzIDwtIGdldC5iYWdnaW5nLmltcG9ydGFuY2UoZmluYWwuYmFnZ2luZy5tb2RlbCkKCgojIFNldCBjdXN0b20gbWFyZ2luczogYm90dG9tLCBsZWZ0LCB0b3AsIHJpZ2h0CnBhcihtYXIgPSBjKDUsIDgsIDQsIDIpKSAgIyBpbmNyZWFzZXMgbGVmdCBtYXJnaW4gZm9yIGxhYmVscwoKCiMgU29ydCBhbmQgcGxvdAppbXBvcnRhbmNlLnNjb3JlcyA8LSBzb3J0KGltcG9ydGFuY2Uuc2NvcmVzLCBkZWNyZWFzaW5nID0gVFJVRSkKYmFycGxvdChpbXBvcnRhbmNlLnNjb3JlcywgaG9yaXogPSBUUlVFLCBsYXMgPSAxLAogICAgICAgIG1haW4gPSAiVmFyaWFibGUgSW1wb3J0YW5jZSAtIEJhZ2dpbmcgKGlwcmVkKSIsCiAgICAgICAgeGxhYiA9ICJJbXBvcnRhbmNlIFNjb3JlIikKIAogCmBgYAoKRmlndXJlIFAuIFRoZSBiYXIgY2hhcnQgZGlzcGxheXMgdGhlIHJlbGF0aXZlIGltcG9ydGFuY2Ugb2YgZWFjaCBwcmVkaWN0b3IgdmFyaWFibGUgaW4gdGhlIGZpbmFsIEJhZ2dpbmcgbW9kZWwsIHdpdGggdGhlIHZhcmlhYmxlcyBvcmRlcmVkIGJ5IHRoZWlyIGNvbnRyaWJ1dGlvbiB0byB0aGUgbW9kZWzigJlzIHByZWRpY3Rpb25zLgoKCgojIyBSYW5kb20gRm9yZXN0IFJlZ3Jlc3Npb24KClJhbmRvbSBGb3Jlc3QgcmVncmVzc2lvbiBpcyBhIHBvd2VyZnVsIGVuc2VtYmxlIGxlYXJuaW5nIHRlY2huaXF1ZSB0aGF0IGFnZ3JlZ2F0ZXMgcHJlZGljdGlvbnMgZnJvbSBtdWx0aXBsZSBkZWNpc2lvbiB0cmVlcyB0byBpbXByb3ZlIGFjY3VyYWN5IGFuZCByb2J1c3RuZXNzLiBJbiB0aGUgcHJvY2VzcywgaHlwZXJwYXJhbWV0ZXJzIHN1Y2ggYXMgdGhlIG51bWJlciBvZiB0cmVlcywgdGhlIG51bWJlciBvZiB2YXJpYWJsZXMgdHJpZWQgYXQgZWFjaCBzcGxpdCwgYW5kIHRoZSBtaW5pbXVtIG5vZGUgc2l6ZSBwbGF5IGEgY3J1Y2lhbCByb2xlIGluIGRldGVybWluaW5nIHRoZSBtb2RlbOKAmXMgcGVyZm9ybWFuY2UuIFRoZSBuZXh0IHN0ZXAgaW52b2x2ZXMgdHVuaW5nIHRoZXNlIGh5cGVycGFyYW1ldGVycyB0byBvcHRpbWl6ZSB0aGUgUmFuZG9tIEZvcmVzdCBtb2RlbCBmb3IgcHJlZGljdGluZyB0b3RhbCBjaG9sZXN0ZXJvbCAodG90Q2hvbCkgdXNpbmcgY3Jvc3MtdmFsaWRhdGlvbiBhbmQgZXZhbHVhdGluZyB0aGUgcmVzdWx0aW5nIHBlcmZvcm1hbmNlLgoKCkh5cGVycGFyYW1ldGVyIHR1bmluZyBmb3IgdGhlIFJhbmRvbSBGb3Jlc3QgbW9kZWwgaW52b2x2ZXMgdGVzdGluZyBkaWZmZXJlbnQgY29tYmluYXRpb25zIG9mIHBhcmFtZXRlcnMsIGluY2x1ZGluZyBtdHJ5LCBudHJlZSwgYW5kIG5vZGVzaXplLCB0byBpZGVudGlmeSB0aGUgb3B0aW1hbCBjb25maWd1cmF0aW9uIGJhc2VkIG9uIFJNU0UgcGVyZm9ybWFuY2UuCgpgYGB7cn0KIyBIeXBlcnBhcmFtZXRlciBUdW5pbmcgZm9yIFJhbmRvbSBGb3Jlc3QKdHVuZV9ncmlkIDwtIGV4cGFuZC5ncmlkKAogIG10cnkgPSBjKDIsIDQsIDYsIDgpLAogIG50cmVlID0gYygxMDAsIDIwMCksCiAgbm9kZXNpemUgPSBjKDEsIDUpCikKCnR1bmVfcmVzdWx0cyA8LSBsaXN0KCkKCmZvciAoaSBpbiAxOm5yb3codHVuZV9ncmlkKSkgewogIHJmX3RlbXAgPC0gcmFuZG9tRm9yZXN0KAogICAgdG90Q2hvbCB+IC4sCiAgICBkYXRhID0gdHJhaW4uZGF0YSwKICAgIG10cnkgPSB0dW5lX2dyaWQkbXRyeVtpXSwKICAgIG50cmVlID0gdHVuZV9ncmlkJG50cmVlW2ldLAogICAgbm9kZXNpemUgPSB0dW5lX2dyaWQkbm9kZXNpemVbaV0KICApCiAgcHJlZHMgPC0gcHJlZGljdChyZl90ZW1wLCBuZXdkYXRhID0gdGVzdC5kYXRhKQogIHJtc2UgPC0gc3FydChtZWFuKCh0ZXN0LmRhdGEkdG90Q2hvbCAtIHByZWRzKV4yKSkKICB0dW5lX3Jlc3VsdHNbW2ldXSA8LSBjKAogICAgcm1zZSA9IHJtc2UsCiAgICBtdHJ5ID0gdHVuZV9ncmlkJG10cnlbaV0sCiAgICBudHJlZSA9IHR1bmVfZ3JpZCRudHJlZVtpXSwKICAgIG5vZGVzaXplID0gdHVuZV9ncmlkJG5vZGVzaXplW2ldCiAgKQp9CgojIENyZWF0ZSBkYXRhIGZyYW1lIGFuZCBmaW5kIGJlc3QgcGFyYW1ldGVycwp0dW5lX2RmIDwtIGFzLmRhdGEuZnJhbWUoZG8uY2FsbChyYmluZCwgdHVuZV9yZXN1bHRzKSkKYmVzdF9wYXJhbXMgPC0gdHVuZV9kZlt3aGljaC5taW4odHVuZV9kZiRybXNlKSwgXQoKCmBgYAoKCgpBZnRlciB0dW5pbmcgdGhlIFJhbmRvbSBGb3Jlc3QgbW9kZWwsIGEgc2VwYXJhdGUgQmFnZ2luZyBtb2RlbCBpcyBidWlsdCBieSBzZXR0aW5nIHRoZSBudW1iZXIgb2YgcHJlZGljdG9ycyBjb25zaWRlcmVkIGF0IGVhY2ggc3BsaXQgZXF1YWwgdG8gdGhlIHRvdGFsIG51bWJlciBvZiBhdmFpbGFibGUgcHJlZGljdG9ycy4gVGhpcyBhcHByb2FjaCBlbXBoYXNpemVzIHJlZHVjaW5nIHZhcmlhbmNlIGJ5IGZ1bGx5IGdyb3dpbmcgZWFjaCB0cmVlIGFuZCBhdmVyYWdpbmcgdGhlaXIgcHJlZGljdGlvbnMgZm9yIG1vcmUgc3RhYmxlIHJlc3VsdHMuCgoKYGBge3J9CgpzZXQuc2VlZCgxMjMpCgojIEJhZ2dpbmcgbW9kZWw6IG10cnkgPSB0b3RhbCBudW1iZXIgb2YgcHJlZGljdG9ycwpiYWdnaW5nX21vZGVsIDwtIHJhbmRvbUZvcmVzdCgKICB0b3RDaG9sIH4gLiwgCiAgZGF0YSA9IHRyYWluLmRhdGEsIAogIG10cnkgPSBuY29sKHRyYWluLmRhdGEpIC0gMSwgICMgRXhjbHVkZSB0YXJnZXQgdmFyaWFibGUKICBpbXBvcnRhbmNlID0gVFJVRQopCgojIFByZWRpY3Qgb24gdGVzdCBkYXRhCmJhZ2dpbmdfcHJlZCA8LSBwcmVkaWN0KGJhZ2dpbmdfbW9kZWwsIG5ld2RhdGEgPSB0ZXN0LmRhdGEpCgojIFBlcmZvcm1hbmNlOiBSTVNFCmJhZ2dpbmdfcm1zZSA8LSBzcXJ0KG1lYW4oKHRlc3QuZGF0YSR0b3RDaG9sIC0gYmFnZ2luZ19wcmVkKV4yKSkKI3ByaW50KHBhc3RlKCJCYWdnaW5nIFJNU0U6Iiwgcm91bmQoYmFnZ2luZ19ybXNlLCAyKSkpCgpgYGAKCgpVc2luZyB0aGUgYmVzdCBoeXBlcnBhcmFtZXRlcnMsIHRoZSBmaW5hbCBSYW5kb20gRm9yZXN0IG1vZGVsIGlzIHRyYWluZWQgb24gdGhlIGZ1bGwgdHJhaW5pbmcgc2V0LiBQcmVkaWN0aW9ucyBhcmUgbWFkZSBvbiB0aGUgdGVzdCBkYXRhLCBhbmQgbW9kZWwgcGVyZm9ybWFuY2UgaXMgYXNzZXNzZWQgdGhyb3VnaCBSTVNFIGFuZCBSLXNxdWFyZWQgdmFsdWVzLgoKCmBgYHtyfQojIEZpbmFsIFJhbmRvbSBGb3Jlc3QgTW9kZWwgd2l0aCBCZXN0IFBhcmFtcwojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpmaW5hbF9yZiA8LSByYW5kb21Gb3Jlc3QoCiAgdG90Q2hvbCB+IC4sIAogIGRhdGEgPSB0cmFpbi5kYXRhLAogIG10cnkgPSBiZXN0X3BhcmFtcyRtdHJ5LAogIG50cmVlID0gYmVzdF9wYXJhbXMkbnRyZWUsCiAgbm9kZXNpemUgPSBiZXN0X3BhcmFtcyRub2Rlc2l6ZSwKICBpbXBvcnRhbmNlID0gVFJVRQopCgojIFByZWRpY3Qgb24gdGVzdCBkYXRhCnJmX3ByZWQgPC0gcHJlZGljdChmaW5hbF9yZiwgbmV3ZGF0YSA9IHRlc3QuZGF0YSkKCiMgQ2FsY3VsYXRlIE1TRQpybXNlLnJmb3Jlc3QgPC0gc3FydChtZWFuKCh0ZXN0LmRhdGEkdG90Q2hvbCAtIHJmX3ByZWQpXjIpKQoKIyBDYWxjdWxhdGUgUi1zcXVhcmVkCnNzLnJlcyA8LSBzdW0oKHRlc3QuZGF0YSR0b3RDaG9sIC0gcmZfcHJlZCleMikKc3MudG90IDwtIHN1bSgobWVhbih0cmFpbi5kYXRhJHRvdENob2wpIC0gdGVzdC5kYXRhJHRvdENob2wpXjIpCnJzcS5yZm9yZXN0IDwtIDEgLSAoc3MucmVzIC8gc3MudG90KQoKcHJpbnQoZmluYWxfcmYpCgpgYGAKVGFibGUgNi4gU3VtbWFyeSBvZiB0aGUgZmluYWwgUmFuZG9tIEZvcmVzdCBtb2RlbCwgZGlzcGxheWluZyB0aGUgbnVtYmVyIG9mIHRyZWVzLCBzZWxlY3RlZCBoeXBlcnBhcmFtZXRlcnMsIG91dC1vZi1iYWcgKE9PQikgZXJyb3IgZXN0aW1hdGUsIGFuZCB2YXJpYWJsZSBpbXBvcnRhbmNlIG1lYXN1cmVzLgoKCgpWYXJpYWJsZSBpbXBvcnRhbmNlIGlzIGV2YWx1YXRlZCBmb3IgdGhlIGZpbmFsIFJhbmRvbSBGb3Jlc3QgbW9kZWwgdG8gaWRlbnRpZnkgdGhlIHByZWRpY3RvcnMgdGhhdCBjb250cmlidXRlIG1vc3QgdG8gdGhlIHByZWRpY3Rpb24gb2YgdG90YWwgY2hvbGVzdGVyb2wuCgpgYGB7cn0KIyBWYXJpYWJsZSBpbXBvcnRhbmNlCmltcG9ydGFuY2UoZmluYWxfcmYpCnZhckltcFBsb3QoZmluYWxfcmYsIG1haW4gPSAiUmFuZG9tIEZvcmVzdCBWYXJpYWJsZSBJbXBvcnRhbmNlIikKCmBgYApUYWJsZSA3LiBWYXJpYWJsZSBpbXBvcnRhbmNlIHRhYmxlLiAKCkZpZ3VyZSBRLiBWYXJpYWJsZSBpbXBvcnRhbmNlIHBsb3QuIAoKVGhlIHZhcmlhYmxlIGltcG9ydGFuY2UgdGFibGUgcHJlc2VudHMgdHdvIGtleSBtZXRyaWNzOiAlSW5jTVNFLCB3aGljaCBtZWFzdXJlcyB0aGUgaW5jcmVhc2UgaW4gbWVhbiBzcXVhcmVkIGVycm9yIHdoZW4gYSB2YXJpYWJsZSBpcyByYW5kb21seSBwZXJtdXRlZCwgYW5kIEluY05vZGVQdXJpdHksIHdoaWNoIHJlZmxlY3RzIHRoZSB0b3RhbCBkZWNyZWFzZSBpbiBub2RlIGltcHVyaXR5IGR1ZSB0byBzcGxpdHMgb24gZWFjaCB2YXJpYWJsZS4gSGlnaGVyIHZhbHVlcyBpbiBlaXRoZXIgbWV0cmljIGluZGljYXRlIGdyZWF0ZXIgaW5mbHVlbmNlIG9uIHRoZSBtb2RlbOKAmXMgcHJlZGljdGl2ZSBhY2N1cmFjeS4gCgpUaGUgYWNjb21wYW55aW5nIFJhbmRvbSBGb3Jlc3QgVmFyaWFibGUgSW1wb3J0YW5jZSBQbG90IHZpc3VhbGx5IHJhbmtzIHRoZSBwcmVkaWN0b3JzIGJhc2VkIG9uIHRoZXNlIHNjb3JlcywgcHJvdmlkaW5nIGFuIGludHVpdGl2ZSB3YXkgdG8gaWRlbnRpZnkgdGhlIG1vc3QgaW1wYWN0ZnVsIGZlYXR1cmVzIGluIHRoZSBtb2RlbOKAmXMgcGVyZm9ybWFuY2UuCgoKCgpUaGUgdGFibGUgb2YgdmFyaWFibGUgaW1wb3J0YW5jZSBmb3IgdGhlIFJhbmRvbSBGb3Jlc3QgbW9kZWwgc2hvd3MgdHdvIGtleSBtZXRyaWNzOiAlSW5jTVNFLCB3aGljaCBpbmRpY2F0ZXMgaG93IG11Y2ggdGhlIG1lYW4gc3F1YXJlZCBlcnJvciBpbmNyZWFzZXMgd2hlbiBlYWNoIHZhcmlhYmxlIGlzIHBlcm11dGVkLCBhbmQgSW5jTm9kZVB1cml0eSwgd2hpY2ggcmVmbGVjdHMgdGhlIHRvdGFsIGRlY3JlYXNlIGluIG5vZGUgaW1wdXJpdHkgZHVlIHRvIGVhY2ggdmFyaWFibGUuIFRvIHZpc3VhbGl6ZSB0aGVzZSByZXN1bHRzLCB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZSBwbG90IHJhbmtzIHRoZSBmZWF0dXJlcyBiYXNlZCBvbiB0aGVpciBpbXBhY3QsIHByb3ZpZGluZyBhbiBpbnR1aXRpdmUgdmlldyBvZiB3aGljaCB2YXJpYWJsZXMgYXJlIG1vc3QgaW5mbHVlbnRpYWwgaW4gcHJlZGljdGluZyB0aGUgdGFyZ2V0LgoKCgojIyBDb21wYXJpc29uOiBNb2RlbCBQZXJmb3JtYW5jZQoKSW4gdGhpcyBhbmFseXNpcywgdmFyaW91cyByZWdyZXNzaW9uIG1vZGVscyBhcmUgZXZhbHVhdGVkIHRvIHByZWRpY3QgdG90YWwgY2hvbGVzdGVyb2wgKHRvdENob2wpIHVzaW5nIHRoZSBGcmFtaW5naGFtIEhlYXJ0IFN0dWR5IGRhdGFzZXQuIFRoZSBtb2RlbHPigJkgcGVyZm9ybWFuY2UgaXMgYXNzZXNzZWQgdGhyb3VnaCBrZXkgbWV0cmljcyBzdWNoIGFzIFJvb3QgTWVhbiBTcXVhcmVkIEVycm9yIChSTVNFKSBhbmQgUi1zcXVhcmVkLCB3aXRoIHRoZSBnb2FsIG9mIGRldGVybWluaW5nIHdoaWNoIG1vZGVsIG1vc3QgYWNjdXJhdGVseSBjYXB0dXJlcyB0aGUgdW5kZXJseWluZyBwYXR0ZXJucyBpbiB0aGUgZGF0YS4KCmBgYHtyfQoKIy0tLSBBc3N1bWVzIHRoZXNlIHZhbHVlcyBhcmUgYWxyZWFkeSBjb21wdXRlZCBhbmQgc3RvcmVkIC0tLQojIG1zZS5iZXN0Y3AsIG1zZS5taW5jcAojIHJzcXVhcmVkLmJlc3RjcCwgcnNxdWFyZWQubWluY3AKIyBtc2UuYmFnZ2luZywgcnNxLmJhZ2dpbmcKIyBtc2UucmZvcmVzdCwgcnNxLnJmb3Jlc3QKCiMgQ2FsY3VsYXRlIExTRSBtb2RlbCAoU3RlcHdpc2UgQUlDKQptb2RlbC5sc2UgPC0gc3RlcChsbSh0b3RDaG9sIH4gLiwgZGF0YSA9IHRyYWluLmRhdGEpLCBkaXJlY3Rpb24gPSAiYm90aCIsIHRyYWNlID0gRkFMU0UpCnByZWQubHNlIDwtIHByZWRpY3QobW9kZWwubHNlLCBuZXdkYXRhID0gdGVzdC5kYXRhKQoKIyBDb21wdXRlIG1ldHJpY3MgZm9yIExTRSBtb2RlbApybXNlLmxzZSA8LSBzcXJ0KG1lYW4oKHRlc3QuZGF0YSR0b3RDaG9sIC0gcHJlZC5sc2UpXjIpKQpzcy5yZXMubHNlIDwtIHN1bSgodGVzdC5kYXRhJHRvdENob2wgLSBwcmVkLmxzZSleMikKc3MudG90LmxzZSA8LSBzdW0oKG1lYW4odHJhaW4uZGF0YSR0b3RDaG9sKSAtIHRlc3QuZGF0YSR0b3RDaG9sKV4yKQpyc3EubHNlIDwtIDEgLSAoc3MucmVzLmxzZSAvIHNzLnRvdC5sc2UpCgojLS0tIENyZWF0ZSBzdW1tYXJ5IHRhYmxlIC0tLQptb2RlbF9yZXN1bHRzIDwtIGRhdGEuZnJhbWUoCiAgTW9kZWwgPSBjKCJQcnVuZWQgVHJlZSAobWluIGNwKSIsIAogICAgICAgICAgICAiUHJ1bmVkIFRyZWUgKDEtU0UgY3ApIiwgCiAgICAgICAgICAgICJCYWdnaW5nIiwgCiAgICAgICAgICAgICJSYW5kb20gRm9yZXN0IiwgCiAgICAgICAgICAgICJTdGVwd2lzZSBBSUMgKExTRSkiKSwKICBSTVNFID0gYyhybXNlLm1pbmNwLCAKICAgICAgICAgIHJtc2UuYmVzdGNwLCAKICAgICAgICAgIHJtc2UuYmFnZ2luZywgCiAgICAgICAgICBybXNlLnJmb3Jlc3QsIAogICAgICAgICAgcm1zZS5sc2UpLAogIFJfc3F1YXJlZCA9IGMocnNxdWFyZWQubWluY3AsIAogICAgICAgICAgICAgICAgcnNxdWFyZWQuYmVzdGNwLCAKICAgICAgICAgICAgICAgIHJzcS5iYWdnaW5nLCAKICAgICAgICAgICAgICAgIHJzcS5yZm9yZXN0LCAKICAgICAgICAgICAgICAgIHJzcS5sc2UpCikKCiMgVmlldyBjb21wYXJpc29uCnByaW50KG1vZGVsX3Jlc3VsdHMpCgpgYGAKClRhYmxlIDguIFJNU0UgYW5kIFItc3F1YXJlZCB2YWx1ZXMgZm9yIHRoZSBmb3VyIHRyZWUgbW9kZWxzLCB3aXRoIFN0ZXB3aXNlIEFJQyAoTFNFKSBhcyB0aGUgYmFzZWxpbmUgZm9yIGNvbXBhcmlzb24uIAoKClJhbmRvbSBGb3Jlc3Qgb3V0cGVyZm9ybWVkIHRoZSBvdGhlciByZWdyZXNzaW9uIG1vZGVscywgYWNoaWV2aW5nIHRoZSBoaWdoZXN0IFItc3F1YXJlZCBhbmQgdGhlIGxvd2VzdCBSTVNFLCBpbmRpY2F0aW5nIGl0cyBzdXBlcmlvciBhYmlsaXR5IHRvIGNhcHR1cmUgdGhlIHJlbGF0aW9uc2hpcHMgd2l0aGluIHRoZSBkYXRhLiBQcnVuZWQgVHJlZSAobWluIGNwKSBmb2xsb3dlZCBjbG9zZWx5IGJlaGluZCwgc2hvd2luZyBjb21wZXRpdGl2ZSBwZXJmb3JtYW5jZSBidXQgc2xpZ2h0bHkgbG93ZXIgcHJlZGljdGl2ZSBhY2N1cmFjeSBjb21wYXJlZCB0byBSYW5kb20gRm9yZXN0LgpjbGVhbmVkIG9mIHJlZHVuZGFuY3kgYnV0IG1ldHJpYyBhcmUgZGlmZmVyZW50IHRoYW4gbXkgY29kZQoKCgoKIyBDQVJUIENMQVNTSUZJQ0FUSU9OCgpDQVJUIChDbGFzc2lmaWNhdGlvbiBhbmQgUmVncmVzc2lvbiBUcmVlcykgY2xhc3NpZmljYXRpb24gY29uc3RydWN0cyBhIGRlY2lzaW9uIHRyZWUgdGhhdCBzcGxpdHMgdGhlIGRhdGEgaW50byBpbmNyZWFzaW5nbHkgaG9tb2dlbmVvdXMgZ3JvdXBzIGJhc2VkIG9uIGEgY2F0ZWdvcmljYWwgb3V0Y29tZSwgc3VjaCBhcyB0aGUgcHJlc2VuY2Ugb3IgYWJzZW5jZSBvZiAxMC15ZWFyIGNvcm9uYXJ5IGhlYXJ0IGRpc2Vhc2UgKFRlblllYXJDSEQpIGluIHRoZSBGcmFtaW5naGFtIEhlYXJ0IFN0dWR5IGRhdGFzZXQuIFRoaXMgYXBwcm9hY2ggaWRlbnRpZmllcyB0aGUgbW9zdCBpbXBvcnRhbnQgcHJlZGljdG9ycyBhbmQgb3B0aW1hbCBzcGxpdCBwb2ludHMgdG8gYmVzdCBjbGFzc2lmeSBpbmRpdmlkdWFscyBhY2NvcmRpbmcgdG8gdGhlaXIgcmlzayBvZiBkZXZlbG9waW5nIENIRCB3aXRoaW4gdGVuIHllYXJzLiBUaGUga2V5IGNvbXBvbmVudHMgZm9yIHRoaXMgYW5hbHlzaXMgYXJlIGxpc3RlZCBiZWxvdy4KCjEpIFNwbGl0dGluZyBDcml0ZXJpb246IFVzZXMgbWVhc3VyZXMgbGlrZSBHaW5pIGltcHVyaXR5IG9yIGVudHJvcHkgdG8gY2hvb3NlIHRoZSBiZXN0IHZhcmlhYmxlIGFuZCBzcGxpdCBwb2ludC4KMikgU3RvcHBpbmcgUnVsZXM6IFBhcmFtZXRlcnMgc3VjaCBhcyBtaW5zcGxpdCwgbWluYnVja2V0LCBhbmQgbWF4ZGVwdGggY29udHJvbCB0cmVlIGdyb3d0aCBhbmQgcHJldmVudCBvdmVyZml0dGluZy4KMykgUHJ1bmluZzogUmVkdWNlcyB0cmVlIGNvbXBsZXhpdHkgdXNpbmcgdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgYW5kIGNyb3NzLXZhbGlkYXRpb24gdG8gc2VsZWN0IGFuIG9wdGltYWwgc3VidHJlZS4KNCkgUHJlZGljdGlvbiBhbmQgRXZhbHVhdGlvbjogVGhlIGZpbmFsIHRyZWUgaXMgdXNlZCB0byBwcmVkaWN0IG91dGNvbWVzIG9uIHRlc3QgZGF0YSwgdHlwaWNhbGx5IGV2YWx1YXRlZCB1c2luZyBhY2N1cmFjeSwgc2Vuc2l0aXZpdHksIHNwZWNpZmljaXR5LCBhbmQgQVVDLgoKCiMjIEluaXRpYWwgQ0FSVCBDbGFzc2lmaWNhdGlvbgoKVGhlIENBUlQgY2xhc3NpZmljYXRpb24gbW9kZWwgZm9yIHByZWRpY3RpbmcgVGVuWWVhckNIRCBpcyBidWlsdCB1c2luZyBhIHN1YnNldCBvZiB0aGUgRnJhbWluZ2hhbSBIZWFydCBTdHVkeSBkYXRhc2V0LiBUaGUgdHJlZSBpcyBjb25zdHJ1Y3RlZCB3aXRoIHNwZWNpZmljIHBhcmFtZXRlcnMgc3VjaCBhcyBsaW1pdGluZyB0aGUgZGVwdGggb2YgdGhlIHRyZWUgYW5kIGVuc3VyaW5nIGEgbWluaW11bSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZvciBzcGxpdHMuIFRoZSByZXN1bHRpbmcgbW9kZWwgcmVwcmVzZW50cyB0aGUgZnVsbCwgdW5wcnVuZWQgdHJlZSwgd2hpY2ggaXMgdGhlbiB2aXN1YWxpemVkIHRvIHNob3cgdGhlIGNsYXNzIHByb2JhYmlsaXRpZXMgYW5kIGxhYmVscyBhdCBlYWNoIG5vZGUsIHByb3ZpZGluZyBpbnNpZ2h0cyBpbnRvIGhvdyBkaWZmZXJlbnQgdmFyaWFibGVzIGNvbnRyaWJ1dGUgdG8gdGhlIHByZWRpY3Rpb24gb2YgMTAteWVhciBjb3JvbmFyeSBoZWFydCBkaXNlYXNlIHJpc2suCgpgYGB7cn0KIyBTZXQgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5CnNldC5zZWVkKDEyMykKCiMgU3BsaXQgZGF0YSBpbnRvIHRyYWluaW5nICg3MCUpIGFuZCB0ZXN0aW5nICgzMCUpIHNldHMKdHJhaW5faW5kZXggPC0gc2FtcGxlKDE6bnJvdyhmaHMpLCBzaXplID0gMC43ICogbnJvdyhmaHMpKQp0cmFpbl9kYXRhIDwtIGZoc1t0cmFpbl9pbmRleCwgXQp0ZXN0X2RhdGEgPC0gZmhzWy10cmFpbl9pbmRleCwgXQoKIyBCdWlsZCBpbml0aWFsIENBUlQgY2xhc3NpZmljYXRpb24gbW9kZWwgKHByZWRpY3RpbmcgVGVuWWVhckNIRCkKY2xhc3NfbW9kZWwgPC0gcnBhcnQoCiAgVGVuWWVhckNIRCB+IC4sIAogIGRhdGEgPSB0cmFpbl9kYXRhLAogIG1ldGhvZCA9ICJjbGFzcyIsICAjIENsYXNzaWZpY2F0aW9uIHRyZWUKICBjb250cm9sID0gcnBhcnQuY29udHJvbCgKICAgIG1pbnNwbGl0ID0gMTAsCiAgICBtaW5idWNrZXQgPSA1LAogICAgY3AgPSAwLjAwMDEsCiAgICBtYXhkZXB0aCA9IDUKICApCikKCiMgVmlzdWFsaXplIHRoZSBjbGFzc2lmaWNhdGlvbiB0cmVlCnJwYXJ0LnBsb3QoY2xhc3NfbW9kZWwsIAogICAgICAgICAgIGV4dHJhID0gMTA0LCAgICAgICAgICAgICAjIFNob3cgcHJvYmFiaWxpdGllcyBhbmQgY2xhc3MgbGFiZWxzCiAgICAgICAgICAgYm94LnBhbGV0dGUgPSAiR25CdSIsICAgICMgR3JlZW4gdG8gYmx1ZSBncmFkaWVudAogICAgICAgICAgIGJyYW5jaC5sdHkgPSAxLCAKICAgICAgICAgICBzaGFkb3cuY29sID0gImdyYXkiLCAKICAgICAgICAgICBubiA9IFRSVUUsICAgICAgICAgICAgICAgIyBTaG93IG5vZGUgbnVtYmVycwogICAgICAgICAgIG1haW4gPSAiSW5pdGlhbCBDQVJUIENsYXNzaWZpY2F0aW9uIFRyZWUgZm9yIFRlblllYXJDSEQiKQoKYGBgCgpGaWd1cmUgUi4gSW5pdGlhbCBDQVJUIERlY2lzaW9uIFRyZWUuIAoKCiMjIFR1bmluZwoKClRoaXMgY29kZSBldmFsdWF0ZXMgdGhlIHR1bmluZyBvZiBhIGNsYXNzaWZpY2F0aW9uIHRyZWUgbW9kZWwgYnkgZXhhbWluaW5nIHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIHZhbHVlcyBmcm9tIHRoZSBtb2RlbOKAmXMgY29zdC1jb21wbGV4aXR5IHRhYmxlIChjcHRhYmxlKS4gSXQgaWRlbnRpZmllcyB0aGUgY3AgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgbWluaW11bSBjcm9zcy12YWxpZGF0aW9uIGVycm9yICh4ZXJyb3IpIGFuZCB0aGUgY3Agd2l0aGluIHRoZSAxLXN0YW5kYXJkIGVycm9yICgxLVNFKSByYW5nZSwgd2hpY2ggaGVscHMgc2VsZWN0IHRoZSBzaW1wbGVzdCB0cmVlIHRoYXQgc3RpbGwgcHJvdmlkZXMgb3B0aW1hbCBwZXJmb3JtYW5jZS4gQnkgdHVuaW5nIHRoZSBjcCwgd2UgYWltIHRvIGJhbGFuY2UgbW9kZWwgY29tcGxleGl0eSBhbmQgcHJlZGljdGl2ZSBhY2N1cmFjeSwgd2l0aCB0aGUgMS1TRSBydWxlIHNlcnZpbmcgYXMgYSBjb25zZXJ2YXRpdmUgYXBwcm9hY2ggdG8gcHJldmVudCBvdmVyZml0dGluZy4gVGhlIG91dHB1dCBwcm92aWRlcyBib3RoIHRoZSBtaW5pbXVtIGNwIGFuZCB0aGUgY3Agd2l0aGluIHRoZSAxLVNFIHJhbmdlLCBvZmZlcmluZyB2YWx1YWJsZSBpbnNpZ2h0cyBpbnRvIHRoZSB0cmFkZS1vZmYgYmV0d2VlbiBhIG1vcmUgY29tcGxleCBtb2RlbCBhbmQgYSBzaW1wbGVyLCBwb3RlbnRpYWxseSBtb3JlIGdlbmVyYWxpemFibGUgb25lLgoKCgoKYGBge3J9CgpwYW5kZXIoY2xhc3NfbW9kZWwkY3B0YWJsZSkKCm1pbi5yb3cgPC0gd2hpY2gubWluKGNsYXNzX21vZGVsJGNwdGFibGVbLCAieGVycm9yIl0pCm1pbi5jcCA8LSBjbGFzc19tb2RlbCRjcHRhYmxlW21pbi5yb3csICJDUCJdCnNlIDwtIGNsYXNzX21vZGVsJGNwdGFibGVbbWluLnJvdywgInhzdGQiXQp4ZXJyb3IuY3V0b2ZmIDwtIGNsYXNzX21vZGVsJGNwdGFibGVbbWluLnJvdywgInhlcnJvciJdICsgc2UKCiMgTm93IGZpbmQgdGhlIHNpbXBsZXN0IChsYXJnZXN0IGNwKSB0cmVlIHdpdGhpbiAxLVNFIHJhbmdlCmNwLjFTRSA8LSBtYXgoY2xhc3NfbW9kZWwkY3B0YWJsZVtjbGFzc19tb2RlbCRjcHRhYmxlWywgInhlcnJvciJdIDw9IHhlcnJvci5jdXRvZmYsICJDUCJdKQoKY2F0KCJtaW4uY3A6IiwgbWluLmNwLCAiXG4iKQpjYXQoImNwLjFTRToiLCBjcC4xU0UsICJcbiIpCgoKIyBDaGVjayB0aGUgbnVtYmVyIG9mIHRlcm1pbmFsIG5vZGVzIChsZWF2ZXMpIGluIGJvdGggdHJlZXMKI2NhdCgiVGVybWluYWwgbm9kZXMgZm9yIHBydW5lZC50cmVlLjFTRToiLCBsZW5ndGgocHJ1bmVkLnRyZWUuMVNFJGZyYW1lJHZhcltwcnVuZWQudHJlZS4xU0UkZnJhbWUkdmFyID09ICI8bGVhZj4iXSksICJcbiIpCiNjYXQoIlRlcm1pbmFsIG5vZGVzIGZvciBwcnVuZWQudHJlZS5taW46IiwgbGVuZ3RoKHBydW5lZC50cmVlLm1pbiRmcmFtZSR2YXJbcHJ1bmVkLnRyZWUubWluJGZyYW1lJHZhciA9PSAiPGxlYWY+Il0pLCAiXG4iKQoKIyBDb21wYXJlIHNwbGl0cyBiZXR3ZWVuIHRoZSB0d28gdHJlZXMgKHN0cnVjdHVyZSkKI3ByaW50KHBydW5lZC50cmVlLjFTRSkKI3ByaW50KHBydW5lZC50cmVlLm1pbikKCiMgUHJpbnQgdGhlIHN0cnVjdHVyZSBvZiB0aGUgcHJ1bmVkIHRyZWVzIHRvIGluc3BlY3QgZGlmZmVyZW5jZXMKI3N1bW1hcnkocHJ1bmVkLnRyZWUuMVNFKQojc3VtbWFyeShwcnVuZWQudHJlZS5taW4pCgoKYGBgCgpUYWJsZSA5LiBDb21wbGV4aXR5IFBhcmFtZXRlciBUYWJsZS4KClRoZSBtaW5pbXVtIGNyb3NzLXZhbGlkYXRlZCBlcnJvciBvY2N1cnJlZCBhdCBhIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgdmFsdWUgb2YgMC4wMTAyLiBVc2luZyB0aGUgMS1TRSBydWxlLCB0aGUgc2ltcGxlc3QgdHJlZSB3aXRoaW4gb25lIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBtaW5pbXVtIGVycm9yIGFsc28gY29ycmVzcG9uZHMgdG8gdGhlIHNhbWUgY3AgdmFsdWUgb2YgMC4wMTAyLiBUaGlzIHN1Z2dlc3RzIHRoYXQgbm8gZnVydGhlciBzaW1wbGlmaWNhdGlvbiBpcyBuZWNlc3NhcnksIGFuZCB0aGUgdHJlZSB3aXRoIGNwID0gMC4wMTAyIGlzIGJvdGggdGhlIG1vc3QgYWNjdXJhdGUgYW5kIHRoZSBtb3N0IHBhcnNpbW9uaW91cyBjaG9pY2UuCgoKVGhlIGZvbGxvd2luZyBwbG90IGRpc3BsYXlzIHRoZSBjcm9zcy12YWxpZGF0aW9uIHJlc3VsdHMgZm9yIHRoZSBjbGFzc2lmaWNhdGlvbiB0cmVlIG1vZGVsLCBpbGx1c3RyYXRpbmcgaG93IHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIGluZmx1ZW5jZXMgdGhlIG1vZGVs4oCZcyBjcm9zcy12YWxpZGF0aW9uIGVycm9yLgoKCmBgYHtyfQojIFBsb3QgdGhlIGNyb3NzLXZhbGlkYXRpb24gcmVzdWx0cwpwbG90Y3AoY2xhc3NfbW9kZWwpCgpgYGAKCkZpZ3VyZSBTLiBDQVJUIENvbXBsZXhpdHkgUGFyYW1ldGVyIChjcCkgUGxvdC4gCgpUaGUgY3Jvc3MtdmFsaWRhdGlvbiBwbG90IHNob3dzIHRoYXQgdGhlIGxvd2VzdCBlcnJvciBvY2N1cnMgYXQgYSB0cmVlIHNpemUgb2YgNSwgY29ycmVzcG9uZGluZyB0byBhIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgb2YgMC4wMDYyLCBzdWdnZXN0aW5nIHRoYXQgdGhpcyB0cmVlIHNpemUgb2ZmZXJzIHRoZSBiZXN0IGJhbGFuY2UgYmV0d2VlbiBtb2RlbCBjb21wbGV4aXR5IGFuZCBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlLgoKCiMjIENBUlQgQ2xhc3NpZmljYXRpb24gKDEtU0UgUnVsZSkKCgpUaGUgcHJ1bmVkIENBUlQgY2xhc3NpZmljYXRpb24gdHJlZSB1c2luZyB0aGUgMS1TRSBydWxlIGlzIGEgbWV0aG9kIGZvciBzaW1wbGlmeWluZyB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbCBieSByZWR1Y2luZyBpdHMgY29tcGxleGl0eSB3aGlsZSBtYWludGFpbmluZyBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlLiBUaGlzIGFwcHJvYWNoIGludm9sdmVzIHNlbGVjdGluZyB0aGUgc21hbGxlc3QgdHJlZSB3aXRoaW4gb25lIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBtaW5pbXVtIGNyb3NzLXZhbGlkYXRpb24gZXJyb3IsIGVuc3VyaW5nIGEgYmFsYW5jZSBiZXR3ZWVuIG1vZGVsIHNpbXBsaWNpdHkgYW5kIGFjY3VyYWN5LiBVc2luZyB0aGUgcGxvdCwgYSBjcCBvZiAwLjAwNjIgaXMgdXNlZCBmb3IgMS1TRSBSdWxlLiAKCmBgYHtyfQoKIyAxLiBGaW5kIHRoZSBvcHRpbWFsIGNwIHZhbHVlIHRoYXQgbWluaW1pemVzIGNyb3NzLXZhbGlkYXRlZCBlcnJvcgptaW4uY3AgPC0gY2xhc3NfbW9kZWwkY3B0YWJsZVt3aGljaC5taW4oY2xhc3NfbW9kZWwkY3B0YWJsZVssICJ4ZXJyb3IiXSksICJDUCJdCgojIDIuIE1hbnVhbGx5IHNwZWNpZnkgdGhlIDEtU0UgY3AgdmFsdWUgKHlvdSBhbHJlYWR5IGZvdW5kIHRoaXMgZWFybGllcikKY3AuMVNFIDwtIDAuMDA2MgoKIyAzLiBQcnVuZSB0aGUgdHJlZSB1c2luZyB0aGUgMS1TRSBjcCB2YWx1ZQpwcnVuZWQudHJlZS4xU0UgPC0gcHJ1bmUoY2xhc3NfbW9kZWwsIGNwID0gY3AuMVNFKQoKIyA0LiBWaXN1YWxpemUgdGhlIHBydW5lZCB0cmVlICgxLVNFIHJ1bGUgdmVyc2lvbikKcnBhcnQucGxvdChwcnVuZWQudHJlZS4xU0UsIAogICAgICAgICAgIGV4dHJhID0gMTA0LCAgICAgICAgICAgICAgIyBTaG93IHByZWRpY3RlZCBjbGFzcyBhbmQgcHJvYmFiaWxpdHkKICAgICAgICAgICBib3gucGFsZXR0ZSA9ICJHbkJ1IiwgICAgICMgR3JlZW4gdG8gYmx1ZSBjb2xvciBzY2hlbWUKICAgICAgICAgICBicmFuY2gubHR5ID0gMSwgCiAgICAgICAgICAgc2hhZG93LmNvbCA9ICJncmF5IiwgCiAgICAgICAgICAgbm4gPSBUUlVFLCAKICAgICAgICAgICBtYWluID0gIlBydW5lZCBDQVJUIENsYXNzaWZpY2F0aW9uIFRyZWUgKDEtU0UgUnVsZSkiKQoKIyA1LiBHZW5lcmF0ZSBwcmVkaWN0aW9ucyBmb3IgcHJ1bmVkIHRyZWUgKDEtU0UgcnVsZSkKcHJlZC5wcm9iLjFTRSA8LSBwcmVkaWN0KHBydW5lZC50cmVlLjFTRSwgdGVzdF9kYXRhLCB0eXBlID0gInByb2IiKVssIDJdCgojIDYuIENyZWF0ZSBST0MgY3VydmUgZm9yIHBydW5lZCB0cmVlICgxLVNFIHJ1bGUpCnJvYy50cmVlLjFTRSA8LSBwUk9DOjpyb2ModGVzdF9kYXRhJFRlblllYXJDSEQsIHByZWQucHJvYi4xU0UpCgojIDcuIEFjY2VzcyB0aGUgQVVDIGRpcmVjdGx5IGZyb20gdGhlIFJPQyBvYmplY3QKYXVjLnRyZWUuMVNFIDwtIHJvYy50cmVlLjFTRSRhdWMKCgojIDguIE9wdGlvbmFsbHksIHByaW50IHRoZSBBVUMKI3ByaW50KHBhc3RlKCJBVUMgZm9yIHBydW5lZCB0cmVlICgxLVNFKToiLCByb3VuZChhdWMudHJlZS4xU0UsIDQpKSkKCgpgYGAKCkZpZ3VyZSBULiBQcnVuZWQgQ0FSVCBDbGFzc2lmaWNhdGlvbiB0cmVlIHVzaW5nIDEtU0UgUnVsZS4KCgoKCiMjIENBUlQgQ2xhc3NpZmljYXRpb24gVHJlZSAoTWluIENQKQoKVGhlIHBydW5lZCBDQVJUIGNsYXNzaWZpY2F0aW9uIHRyZWUgaXMgdmlzdWFsaXplZCB1c2luZyB0aGUgbWluaW11bSBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIHZhbHVlLCB3aGljaCBoZWxwcyBpbiByZWR1Y2luZyBtb2RlbCBjb21wbGV4aXR5IHdoaWxlIHJldGFpbmluZyBwcmVkaWN0aXZlIGFjY3VyYWN5LiBUaGlzIHZpc3VhbGl6YXRpb24gaGlnaGxpZ2h0cyB0aGUgY2xhc3MgcHJvYmFiaWxpdGllcyBhbmQgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgYXQgZWFjaCBub2RlLCBwcm92aWRpbmcgYSBjbGVhcmVyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIG1vZGVs4oCZcyBkZWNpc2lvbi1tYWtpbmcgcHJvY2Vzcy4KCmBgYHtyfQoKIyBQcnVuZSB0aGUgdHJlZSB1c2luZyB0aGUgY3AgdGhhdCBnaXZlcyBtaW5pbXVtIGNyb3NzLXZhbGlkYXRlZCBlcnJvcgpwcnVuZWQudHJlZS5taW4gPC0gcHJ1bmUoY2xhc3NfbW9kZWwsIGNwID0gbWluLmNwKQoKIyBWaXN1YWxpemUgdGhlIHBydW5lZCB0cmVlIHdpdGggdGhlIGNob3NlbiBtaW5pbXVtIGNwCnJwYXJ0LnBsb3QocHJ1bmVkLnRyZWUubWluLCAKICAgICAgICAgICBleHRyYSA9IDEwNCwgICMgRGlzcGxheXMgbW9yZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiBvbiBub2RlcyAoY2xhc3MgcHJvYmFiaWxpdGllcywgbnVtYmVyIG9mIG9ic2VydmF0aW9ucykKICAgICAgICAgICBib3gucGFsZXR0ZSA9ICJHbkJ1IiwgICMgR3JlZW4gdG8gQmx1ZSBjb2xvciBzY2hlbWUgZm9yIG5vZGUgYm94ZXMKICAgICAgICAgICBicmFuY2gubHR5ID0gMSwgICMgU29saWQgbGluZXMgZm9yIGJyYW5jaGVzCiAgICAgICAgICAgc2hhZG93LmNvbCA9ICJncmF5IiwgICMgU2hhZG93IGNvbG9yIGZvciBub2RlIGxhYmVscwogICAgICAgICAgIG5uID0gVFJVRSwgICMgQWRkIG5vZGUgbnVtYmVycyB0byB0aGUgdHJlZQogICAgICAgICAgIG1haW4gPSAiUHJ1bmVkIENBUlQgQ2xhc3NpZmljYXRpb24gVHJlZSAoTWluIENQKSIgICMgVGl0bGUgZm9yIHRoZSBwbG90CikKCgoKCmBgYAoKRmlndXJlIFUuIFBydW5lZCBDQVJUIENsYXNzaWZpY2F0aW9uIHRyZWUgdXNpbmcgbWluIENQLgoKVGhlIHBydW5lZCBDQVJUIGNsYXNzaWZpY2F0aW9uIHRyZWUgdXNpbmcgdGhlIG1pbmltdW0gQ1AgcmVzdWx0ZWQgaW4gYSBzaW5nbGUgbm9kZSwgc3VnZ2VzdGluZyB0aGF0IHRoaXMgYXBwcm9hY2ggZGlkIG5vdCBwcm9kdWNlIGEgbWVhbmluZ2Z1bCBvciBpbmZvcm1hdGl2ZSBtb2RlbC4KCgoKIyMgQkFHR0lORyBDYXJ0IENsYXNzaWZpY2F0aW9uCgpUaGUgQkFHR0lORyAoQm9vdHN0cmFwIEFnZ3JlZ2F0aW5nKSBtZXRob2QgaXMgYXBwbGllZCB0byB0aGUgQ0FSVCAoQ2xhc3NpZmljYXRpb24gYW5kIFJlZ3Jlc3Npb24gVHJlZXMpIG1vZGVsIHRvIHByZWRpY3QgVGVuWWVhckNIRCwgYSBiaW5hcnkgb3V0Y29tZSBpbmRpY2F0aW5nIHRoZSBsaWtlbGlob29kIG9mIGNhcmRpb3Zhc2N1bGFyIGRpc2Vhc2Ugd2l0aGluIHRlbiB5ZWFycy4gQkFHR0lORyBjb21iaW5lcyBtdWx0aXBsZSBkZWNpc2lvbiB0cmVlcyBidWlsdCBvbiBib290c3RyYXBwZWQgc3Vic2V0cyBvZiB0aGUgZGF0YSwgcmVkdWNpbmcgdmFyaWFuY2UgYW5kIGltcHJvdmluZyBtb2RlbCByb2J1c3RuZXNzLiBCeSBhdmVyYWdpbmcgdGhlIHByZWRpY3Rpb25zIG9mIGluZGl2aWR1YWwgdHJlZXMsIEJBR0dJTkcgaGVscHMgbWl0aWdhdGUgb3ZlcmZpdHRpbmcsIG1ha2luZyBpdCBwYXJ0aWN1bGFybHkgdXNlZnVsIGZvciBjb21wbGV4IGRhdGFzZXRzIGxpa2UgdGhlIEZyYW1pbmdoYW0gSGVhcnQgU3R1ZHkuIFRoZSBhbmFseXNpcyBleHBsb3JlcyB0aGUgbW9kZWzigJlzIHBlcmZvcm1hbmNlIHRocm91Z2ggY3Jvc3MtdmFsaWRhdGlvbiwgdHVuaW5nIGh5cGVycGFyYW1ldGVycywgYW5kIGV2YWx1YXRpbmcgdGhlIHJlc3VsdHMgYmFzZWQgb24gUk1TRSBhbmQgUi1zcXVhcmVkLCB1bHRpbWF0ZWx5IGlkZW50aWZ5aW5nIHRoZSBvcHRpbWFsIHBhcmFtZXRlcnMgZm9yIGFjY3VyYXRlIHByZWRpY3Rpb25zLgoKCkZpcnN0LCB0aGUgQkFHR0lORyBtb2RlbCBpcyB0cmFpbmVkIHVzaW5nIHRoZSBjaG9zZW4gdHJhaW5pbmcgZGF0YSBhbmQgYWdncmVnYXRlIHRoZSBwcmVkaWN0aW9ucyBmcm9tIGVhY2ggZGVjaXNpb24gdHJlZSB0byBtYWtlIHRoZSBmaW5hbCBjbGFzc2lmaWNhdGlvbiwgZW5zdXJpbmcgdGhhdCBlYWNoIG1vZGVs4oCZcyBwcmVkaWN0aW9uIGNvbnRyaWJ1dGVzIHRvIHRoZSBvdmVyYWxsIG91dHB1dC4KCmBgYHtyfQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMTIzKQoKIyBFbnN1cmUgdGhlIHRhcmdldCBpcyBhIGZhY3RvcgpmaHMkVGVuWWVhckNIRCA8LSBhcy5mYWN0b3IoZmhzJFRlblllYXJDSEQpCgojIFNwbGl0IGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzCnRyYWluSW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihmaHMkVGVuWWVhckNIRCwgcCA9IDAuNywgbGlzdCA9IEZBTFNFKQp0cmFpbkRhdGEgPC0gZmhzW3RyYWluSW5kZXgsIF0KdGVzdERhdGEgPC0gZmhzWy10cmFpbkluZGV4LCBdCgojIENyZWF0ZSBoeXBlcnBhcmFtZXRlciBncmlkCmh5cGVyLmdyaWQgPC0gZXhwYW5kLmdyaWQoCiAgbmJhZ2cgPSBjKDI1LCA1MCwgMTAwKSwKICBtaW5zcGxpdCA9IGMoNSwgMTAsIDIwKSwKICBtYXhkZXB0aCA9IGMoNSwgMTAsIDIwKSwKICBjcCA9IGMoMC4wMSwgMC4wMDEpCikKCiMgSW5pdGlhbGl6ZSB0cmFja2luZyB2YXJpYWJsZXMKcmVzdWx0cyA8LSBkYXRhLmZyYW1lKCkKYmVzdC5hY2N1cmFjeSA8LSAwCmJlc3QucGFyYW1zIDwtIGxpc3QoKQoKIyBMb29wIHRocm91Z2ggaHlwZXJwYXJhbWV0ZXIgY29tYmluYXRpb25zCmZvcihpIGluIDE6bnJvdyhoeXBlci5ncmlkKSkgewogIHBhcmFtcyA8LSBoeXBlci5ncmlkW2ksIF0KICAKICBjdHJsIDwtIHJwYXJ0LmNvbnRyb2woCiAgICBtaW5zcGxpdCA9IHBhcmFtcyRtaW5zcGxpdCwKICAgIG1heGRlcHRoID0gcGFyYW1zJG1heGRlcHRoLAogICAgY3AgPSBwYXJhbXMkY3AKICApCiAgCiAgIyBUcmFpbiBCYWdnaW5nIG1vZGVsCiAgYmFnLm1vZGVsIDwtIGJhZ2dpbmcoCiAgICBUZW5ZZWFyQ0hEIH4gLiwKICAgIGRhdGEgPSB0cmFpbkRhdGEsCiAgICBuYmFnZyA9IHBhcmFtcyRuYmFnZywKICAgIGNvb2IgPSBUUlVFLAogICAgY29udHJvbCA9IGN0cmwKICApCiAgCiAgIyBQcmVkaWN0IG9uIHRlc3Qgc2V0CiAgcHJlZHMgPC0gcHJlZGljdChiYWcubW9kZWwsIG5ld2RhdGEgPSB0ZXN0RGF0YSkKICAKICAjIEFjY3VyYWN5CiAgY20gPC0gY29uZnVzaW9uTWF0cml4KHByZWRzLCB0ZXN0RGF0YSRUZW5ZZWFyQ0hEKQogIGFjY3VyYWN5IDwtIGNtJG92ZXJhbGxbIkFjY3VyYWN5Il0KICAKICAjIFN0b3JlIHJlc3VsdHMKICByZXN1bHRzIDwtIHJiaW5kKHJlc3VsdHMsIGRhdGEuZnJhbWUoCiAgICBuYmFnZyA9IHBhcmFtcyRuYmFnZywKICAgIG1pbnNwbGl0ID0gcGFyYW1zJG1pbnNwbGl0LAogICAgbWF4ZGVwdGggPSBwYXJhbXMkbWF4ZGVwdGgsCiAgICBjcCA9IHBhcmFtcyRjcCwKICAgIEFjY3VyYWN5ID0gYWNjdXJhY3kKICApKQogIAogICMgVHJhY2sgYmVzdCByZXN1bHQKICBpZihhY2N1cmFjeSA+IGJlc3QuYWNjdXJhY3kpIHsKICAgIGJlc3QuYWNjdXJhY3kgPC0gYWNjdXJhY3kKICAgIGJlc3QucGFyYW1zIDwtIHBhcmFtcwogIH0KfQoKIyBEaXNwbGF5IGJlc3QgcGFyYW1ldGVycwpjYXQoIkJlc3QgQkFHR0lORyBoeXBlcnBhcmFtZXRlcnM6XG4iKQpwYW5kZXIoYmVzdC5wYXJhbXMpCgojIE9wdGlvbmFsOiBWaWV3IGZ1bGwgcmVzdWx0cwojIHBhbmRlcihyZXN1bHRzKQoKCmBgYAoKVGFibGUgMTAuIFRyYWluIGZpbmFsIEJBR0dJTkcgQ2xhc3NpZmljYXRpb24gbW9kZWwgd2l0aCBiZXN0IGh5cGVycGFyYW1ldGVycwoKClRoZSBvdXRwdXQgc2hvd3MgdGhlIG9wdGltYWwgaHlwZXJwYXJhbWV0ZXJzIGZvciB0aGUgQkFHR0lORyBtb2RlbCwgd2l0aCAxMDAgdHJlZXMgKG5iYWdnKSwgYSBtaW5pbXVtIHNwbGl0IG9mIDIwIG9ic2VydmF0aW9ucyAobWluc3BsaXQpLCBhIG1heGltdW0gZGVwdGggb2YgMjAgZm9yIGVhY2ggdHJlZSAobWF4ZGVwdGgpLCBhbmQgYSBjb21wbGV4aXR5IHBhcmFtZXRlciAoY3ApIG9mIDAuMDAxLgoKCk5leHQgdGhlIGZpbmFsIEJBR0dJTkcgbW9kZWwgaXMgdHJhaW5lZCB1c2luZyB0aGUgYmVzdCBoeXBlcnBhcmFtZXRlcnMgaWRlbnRpZmllZCBkdXJpbmcgdHVuaW5nLiBUaGUgbW9kZWwgaXMgdGhlbiB1c2VkIHRvIHByZWRpY3QgdGhlIHRlc3QgZGF0YSwgYW5kIHRoZSByZXN1bHRzIGFyZSBldmFsdWF0ZWQgdXNpbmcgYSBsYWJlbGVkIGNvbmZ1c2lvbiBtYXRyaXggdG8gYXNzZXNzIHRoZSBjbGFzc2lmaWNhdGlvbiBhY2N1cmFjeSBmb3IgcHJlZGljdGluZyBUZW5ZZWFyQ0hELgoKYGBge3J9CiMgU2V0IHJwYXJ0IGNvbnRyb2wgdXNpbmcgYmVzdCBwYXJhbWV0ZXJzIGZyb20gdHVuaW5nCmJlc3QuY29udHJvbCA8LSBycGFydC5jb250cm9sKAogIG1pbnNwbGl0ID0gYmVzdC5wYXJhbXMkbWluc3BsaXQsCiAgbWF4ZGVwdGggPSBiZXN0LnBhcmFtcyRtYXhkZXB0aCwKICBjcCA9IGJlc3QucGFyYW1zJGNwCikKCiMgVHJhaW4gdGhlIGZpbmFsIEJhZ2dpbmcgbW9kZWwgb24gdHJhaW5pbmcgZGF0YQpmaW5hbC5iYWcubW9kZWwgPC0gYmFnZ2luZygKICBUZW5ZZWFyQ0hEIH4gLiwKICBkYXRhID0gdHJhaW5EYXRhLAogIG5iYWdnID0gYmVzdC5wYXJhbXMkbmJhZ2csCiAgY29vYiA9IFRSVUUsCiAgY29udHJvbCA9IGJlc3QuY29udHJvbAopCgojIEdlbmVyYXRlIHByZWRpY3Rpb25zIChwcm9iYWJpbGl0aWVzKSBmb3IgdGhlIEJhZ2dpbmcgbW9kZWwKcHJlZC5iYWcucHJvYiA8LSBwcmVkaWN0KGZpbmFsLmJhZy5tb2RlbCwgbmV3ZGF0YSA9IHRlc3REYXRhLCB0eXBlID0gInByb2IiKVssIDJdICAjIFsgLDJdIHNlbGVjdHMgdGhlIHByb2JhYmlsaXR5IGZvciB0aGUgcG9zaXRpdmUgY2xhc3MgKDEpCgojIENvbXB1dGUgUk9DIGZvciBCYWdnaW5nIG1vZGVsCnJvYy5iYWdnaW5nIDwtIHJvYyh0ZXN0RGF0YSRUZW5ZZWFyQ0hELCBwcmVkLmJhZy5wcm9iKQoKIyBDb21wdXRlIEFVQyBmb3IgQmFnZ2luZyBtb2RlbAphdWMuYmFnZ2luZyA8LSByb2MuYmFnZ2luZyRhdWMKCiMgUGxvdCBST0MgY3VydmUgZm9yIHRoZSBCYWdnaW5nIG1vZGVsCiNwbG90KHJvYy5iYWdnaW5nLCBtYWluID0gIlJPQyBDdXJ2ZSAtIEJhZ2dpbmciLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpCgojIE5vdyBwcm9jZWVkIHdpdGggY29tcGFyaW5nIG90aGVyIG1vZGVscyBhbmQgcGxvdHRpbmcgYXMgbmVlZGVkCgoKYGBgCgoKCiMjIFJhbmRvbSBGb3Jlc3QgQ2xhc3NpZmljYXRpb24KClJhbmRvbSBGb3Jlc3QgY2xhc3NpZmljYXRpb24gaXMgYW4gZW5zZW1ibGUgbGVhcm5pbmcgbWV0aG9kIHRoYXQgYnVpbGRzIG11bHRpcGxlIGRlY2lzaW9uIHRyZWVzIGFuZCBhZ2dyZWdhdGVzIHRoZWlyIHByZWRpY3Rpb25zIHRvIGltcHJvdmUgY2xhc3NpZmljYXRpb24gYWNjdXJhY3kuIEVhY2ggdHJlZSBpcyB0cmFpbmVkIG9uIGEgcmFuZG9tIHN1YnNldCBvZiB0aGUgZGF0YSB3aXRoIHJhbmRvbSBmZWF0dXJlIHNlbGVjdGlvbiBhdCBlYWNoIHNwbGl0LCB3aGljaCBoZWxwcyByZWR1Y2Ugb3ZlcmZpdHRpbmcgYW5kIGluY3JlYXNlcyB0aGUgbW9kZWzigJlzIHJvYnVzdG5lc3MuIFRoZSBmaW5hbCBwcmVkaWN0aW9uIGlzIGRldGVybWluZWQgYnkgdGhlIG1ham9yaXR5IHZvdGUgb2YgdGhlIGluZGl2aWR1YWwgdHJlZXMsIG1ha2luZyBSYW5kb20gRm9yZXN0IGEgcG93ZXJmdWwgdG9vbCBmb3IgY2xhc3NpZmljYXRpb24gdGFza3MsIGVzcGVjaWFsbHkgd2hlbiBkZWFsaW5nIHdpdGggY29tcGxleCBhbmQgaGlnaC1kaW1lbnNpb25hbCBkYXRhLgoKSHlwZXJwYXJhbWV0ZXIgdHVuaW5nIGludm9sdmVzIHNlbGVjdGluZyB0aGUgb3B0aW1hbCBzZXR0aW5ncyBmb3IgYSBtb2RlbOKAmXMgcGFyYW1ldGVycywgc3VjaCBhcyB0aGUgbnVtYmVyIG9mIHRyZWVzLCB0aGUgbWF4aW11bSBkZXB0aCBvZiBlYWNoIHRyZWUsIGFuZCB0aGUgbWluaW11bSBudW1iZXIgb2Ygc2FtcGxlcyByZXF1aXJlZCBmb3IgYSBzcGxpdCwgdG8gZW5oYW5jZSBpdHMgcGVyZm9ybWFuY2UgYW5kIGFjY3VyYWN5LiAKCmBgYHtyfQoKc2V0LnNlZWQoMTIzKQoKIyBEcm9wIEJQTWVkc19vcmlnaW5hbCBmcm9tIGJvdGggc2V0cwp0cmFpbi5kYXRhIDwtIHRyYWluLmRhdGFbLCAhY29sbmFtZXModHJhaW4uZGF0YSkgJWluJSAiQlBNZWRzX29yaWdpbmFsIl0KdGVzdC5kYXRhIDwtIHRlc3QuZGF0YVssICFjb2xuYW1lcyh0ZXN0LmRhdGEpICVpbiUgIkJQTWVkc19vcmlnaW5hbCJdCgojIEVuc3VyZSBzYW1lIGNvbHVtbnMgYW5kIGNvbHVtbiBvcmRlcgp0ZXN0LmRhdGEgPC0gdGVzdC5kYXRhWywgbmFtZXModHJhaW4uZGF0YSldCgojIEVuc3VyZSBhbGwgZmFjdG9yIGxldmVscyBhcmUgYWxpZ25lZApmb3IgKGNvbCBpbiBuYW1lcyh0cmFpbi5kYXRhKSkgewogIGlmIChpcy5mYWN0b3IodHJhaW4uZGF0YVtbY29sXV0pKSB7CiAgICB0ZXN0LmRhdGFbW2NvbF1dIDwtIGZhY3Rvcih0ZXN0LmRhdGFbW2NvbF1dLCBsZXZlbHMgPSBsZXZlbHModHJhaW4uZGF0YVtbY29sXV0pKQogIH0KfQoKIyBUd28td2F5IHNwbGl0OiB0cmFpbmluZy90ZXN0aW5nCnRyYWluLmlkeCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGZocyRUZW5ZZWFyQ0hELCBwID0gMC43LCBsaXN0ID0gRkFMU0UpCnRyYWluLmRhdGEgPC0gZmhzW3RyYWluLmlkeCwgXQp0ZXN0LmRhdGEgPC0gZmhzWy10cmFpbi5pZHgsIF0KCnRlc3QuZGF0YSA8LSB0ZXN0LmRhdGFbLCBuYW1lcyh0cmFpbi5kYXRhKV0KCiMgRW5zdXJlIG91dGNvbWUgaXMgZmFjdG9yIHdpdGggbGV2ZWxzICIwIiwgIjEiCnRyYWluLmRhdGEkVGVuWWVhckNIRCA8LSBhcy5mYWN0b3IodHJhaW4uZGF0YSRUZW5ZZWFyQ0hEKQp0ZXN0LmRhdGEkVGVuWWVhckNIRCA8LSBhcy5mYWN0b3IodGVzdC5kYXRhJFRlblllYXJDSEQpCiMgRW5zdXJlICdCUE1lZHNfb3JpZ2luYWwnIGlzIG5vdCBpbiB0aGUgZGF0YQp0cmFpbi5kYXRhIDwtIHRyYWluLmRhdGFbLCAhY29sbmFtZXModHJhaW4uZGF0YSkgJWluJSAiQlBNZWRzX29yaWdpbmFsIl0KCiMgQ3Jvc3MtdmFsaWRhdGlvbiBzZXR1cAprIDwtIDUKdHJhaW4uc2l6ZSA8LSBucm93KHRyYWluLmRhdGEpCmZvbGQuc2l6ZSA8LSBmbG9vcih0cmFpbi5zaXplIC8gaykKCiMgSHlwZXJwYXJhbWV0ZXIgZ3JpZAp0dW5lLmdyaWQgPC0gZXhwYW5kLmdyaWQoCiAgbXRyeSA9IGMoMiwgMywgNCwgNSksCiAgbnRyZWUgPSBjKDEwMCwgMzAwLCA1MDApLAogIG5vZGVzaXplID0gYygxLCAzLCA1LCAxMCksCiAgbWF4bm9kZXMgPSBjKDUsIDEwLCAyMCwgTkEpCikKCiMgRW5zdXJlIHZhbGlkIG10cnkgdmFsdWVzIGJlZm9yZSBzdGFydGluZwpwIDwtIG5jb2wodHJhaW4uZGF0YSkgLSAxICAjIE51bWJlciBvZiBwcmVkaWN0b3JzCnR1bmUuZ3JpZCA8LSBzdWJzZXQodHVuZS5ncmlkLCBtdHJ5ID49IDEgJiBtdHJ5IDw9IHAgJiBub2Rlc2l6ZSA8IG1heG5vZGVzKQoKIyBJbml0aWFsaXplIHJlc3VsdHMgc3RvcmFnZQpyZXN1bHRzIDwtIGRhdGEuZnJhbWUoKQpiZXN0LmF1YyA8LSAwCmJlc3QuaHlwLnBhcmFtcyA8LSBOVUxMCgojIEdyaWQgc2VhcmNoIHdpdGggY3Jvc3MtdmFsaWRhdGlvbgpmb3IgKGkgaW4gMTpucm93KHR1bmUuZ3JpZCkpIHsKICBjdXJyZW50LnR1bmUucGFyYW1zIDwtIHR1bmUuZ3JpZFtpLCBdCiAgY3YuYXVjIDwtIG51bWVyaWMoaykgICMgUHJlYWxsb2NhdGUgQVVDIHZlY3RvciBmb3IgZm9sZHMKICAKICBmb3IgKGogaW4gMTprKSB7CiAgICAjIERlZmluZSB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbiBzZXRzCiAgICBjdi5pZCA8LSAoKGogLSAxKSAqIGZvbGQuc2l6ZSArIDEpOihqICogZm9sZC5zaXplKQogICAgY3YudHJhaW4gPC0gdHJhaW4uZGF0YVstY3YuaWQsIF0KICAgIGN2LnZhbGlkIDwtIHRyYWluLmRhdGFbY3YuaWQsIF0KICAgIAogICAgIyBUcmFpbiB0aGUgbW9kZWwKICAgIHJmLmN2IDwtIHJhbmRvbUZvcmVzdCgKICAgICAgVGVuWWVhckNIRCB+IC4sCiAgICAgIGRhdGEgPSBjdi50cmFpbiwKICAgICAgbXRyeSA9IGN1cnJlbnQudHVuZS5wYXJhbXMkbXRyeSwKICAgICAgbnRyZWUgPSBjdXJyZW50LnR1bmUucGFyYW1zJG50cmVlLAogICAgICBub2Rlc2l6ZSA9IGN1cnJlbnQudHVuZS5wYXJhbXMkbm9kZXNpemUsCiAgICAgIG1heG5vZGVzID0gY3VycmVudC50dW5lLnBhcmFtcyRtYXhub2RlcwogICAgKQogICAgCiAgICAjIFByZWRpY3QgcHJvYmFiaWxpdGllcyBmb3IgcG9zaXRpdmUgY2xhc3MgJzEnCiAgICBwcm9iLmN2IDwtIHByZWRpY3QocmYuY3YsIG5ld2RhdGEgPSBjdi52YWxpZCwgdHlwZSA9ICJwcm9iIilbLCAiMSJdCiAgICAKICAgICMgQ29tcHV0ZSBBVUMgdXNpbmcgcFJPQwogICAgcm9jX29iaiA8LSBwUk9DOjpyb2MoCiAgICAgIHJlc3BvbnNlID0gY3YudmFsaWQkVGVuWWVhckNIRCwKICAgICAgcHJlZGljdG9yID0gcHJvYi5jdiwKICAgICAgZGlyZWN0aW9uID0gIjwiCiAgICApCiAgICBjdi5hdWNbal0gPC0gcFJPQzo6YXVjKHJvY19vYmopCiAgfQogIAogICMgQ29tcHV0ZSBhdmVyYWdlIEFVQyBvdmVyIGZvbGRzCiAgYXZnLmF1YyA8LSBtZWFuKGN2LmF1YykKICAKICAjIFN0b3JlIHJlc3VsdHMKICByZXN1bHRzIDwtIHJiaW5kKHJlc3VsdHMsIGRhdGEuZnJhbWUoCiAgICBtdHJ5ID0gY3VycmVudC50dW5lLnBhcmFtcyRtdHJ5LAogICAgbnRyZWUgPSBjdXJyZW50LnR1bmUucGFyYW1zJG50cmVlLAogICAgbm9kZXNpemUgPSBjdXJyZW50LnR1bmUucGFyYW1zJG5vZGVzaXplLAogICAgbWF4bm9kZXMgPSBjdXJyZW50LnR1bmUucGFyYW1zJG1heG5vZGVzLAogICAgYXVjID0gYXZnLmF1YwogICkpCiAgCiAgIyBVcGRhdGUgYmVzdCBpZiBjdXJyZW50IGlzIGJldHRlcgogIGlmIChhdmcuYXVjID4gYmVzdC5hdWMpIHsKICAgIGJlc3QuYXVjIDwtIGF2Zy5hdWMKICAgIGJlc3QuaHlwLnBhcmFtcyA8LSBjdXJyZW50LnR1bmUucGFyYW1zCiAgfQp9CgojIERpc3BsYXkgYmVzdCBwYXJhbWV0ZXJzCnBhbmRlcihkYXRhLmZyYW1lKGNiaW5kKGJlc3QuaHlwLnBhcmFtcywgYmVzdC5hdWMpKSkKCgoKYGBgCgpUYWJsZSAxMS4gUmFuZG9tIEZvcmVzdCBIeXBlcnBhcmFtZXRlciBUdW5pbmcgUmVzdWx0cyBUYWJsZQoKClRoZSBvdXRwdXQgaW5kaWNhdGVzIHRoZSBvcHRpbWFsIGh5cGVycGFyYW1ldGVycyBmb3IgdGhlIFJhbmRvbSBGb3Jlc3QgbW9kZWwsIHdpdGggbXRyeSBzZXQgdG8gNCwgbnRyZWUgc2V0IHRvIDUwMCwgbm9kZXNpemUgc2V0IHRvIDEwLCBhbmQgbWF4bm9kZXMgc2V0IHRvIDIwLiBUaGVzZSBzZXR0aW5ncyByZXN1bHRlZCBpbiBhIGJlc3QgQVVDIG9mIDAuNjk2NSwgd2hpY2ggcmVmbGVjdHMgdGhlIG1vZGVs4oCZcyBwZXJmb3JtYW5jZSBpbiBkaXN0aW5ndWlzaGluZyBiZXR3ZWVuIHRoZSBjbGFzc2VzLgoKCgpUbyBmaW5hbGl6ZSB0aGUgUmFuZG9tIEZvcmVzdCBtb2RlbCwgdGhlIGJlc3QgaHlwZXJwYXJhbWV0ZXJzIGlkZW50aWZpZWQgZnJvbSB0dW5pbmcgYXJlIHVzZWQgdG8gdHJhaW4gdGhlIG1vZGVsIG9uIHRoZSBmdWxsIHRyYWluaW5nIGRhdGFzZXQuIFByZWRpY3Rpb25zIGZvciBjbGFzcyBwcm9iYWJpbGl0aWVzIG9mIHRoZSBwb3NpdGl2ZSBjbGFzcyAo4oCcMeKAnSkgYXJlIHRoZW4gbWFkZSBmb3IgdGhlIHRlc3QgZGF0YS4gSW4gdGhlIHN1YnNlcXVlbnQgc3RlcHMsIHRoZSBtb2RlbOKAmXMgcGVyZm9ybWFuY2Ugd2lsbCBiZSBhc3Nlc3NlZCB1c2luZyB0aGUgUk9DIGN1cnZlIGFuZCBBVUMsIHdoaWNoIHdpbGwgYmUgcHJlc2VudGVkIGR1cmluZyB0aGUgY29tcGFyaXNvbiBzZWN0aW9uLgoKCmBgYHtyfQoKIyBUcmFpbiB0aGUgZmluYWwgUmFuZG9tIEZvcmVzdCBtb2RlbCB1c2luZyBiZXN0IHBhcmFtZXRlcnMKZmluYWwucmYuY2xzIDwtIHJhbmRvbUZvcmVzdCgKICBUZW5ZZWFyQ0hEIH4gLiwKICBkYXRhID0gdHJhaW4uZGF0YSwKICBudHJlZSA9IGJlc3QuaHlwLnBhcmFtcyRudHJlZSwKICBtdHJ5ID0gYmVzdC5oeXAucGFyYW1zJG10cnksCiAgbm9kZXNpemUgPSBiZXN0Lmh5cC5wYXJhbXMkbm9kZXNpemUsCiAgbWF4bm9kZXMgPSBiZXN0Lmh5cC5wYXJhbXMkbWF4bm9kZXMsCiAgaW1wb3J0YW5jZSA9IFRSVUUKKQoKIyBQcmVkaWN0IGNsYXNzIHByb2JhYmlsaXRpZXMgZm9yIHRoZSBwb3NpdGl2ZSBjbGFzcyAiMSIKcHJlZC5yZi5wcm9iIDwtIHByZWRpY3QoZmluYWwucmYuY2xzLCB0ZXN0LmRhdGEsIHR5cGUgPSAicHJvYiIpWywgIjEiXQoKIyBFbnN1cmUgcmVzcG9uc2UgaXMgYSBmYWN0b3Igd2l0aCBjb3JyZWN0IGxldmVscwp0ZXN0LnJlc3BvbnNlIDwtIGZhY3Rvcih0ZXN0LmRhdGEkVGVuWWVhckNIRCwgbGV2ZWxzID0gYygwLCAxKSkKCiMgQ29tcHV0ZSBST0MgYW5kIEFVQwpyZi5yb2MgPC0gcFJPQzo6cm9jKHJlc3BvbnNlID0gdGVzdC5yZXNwb25zZSwgcHJlZGljdG9yID0gcHJlZC5yZi5wcm9iLCBsZXZlbHMgPSBjKCIwIiwgIjEiKSwgZGlyZWN0aW9uID0gIjwiKQpyZi5hdWMgPC0gcFJPQzo6YXVjKHJmLnJvYykKCiMgRGlzcGxheSBBVUMKI3ByaW50KHJmLmF1YykKCgoKYGBgCgoKCgoKClRoZSBpbXBvcnRhbmNlIG9mIGVhY2ggcHJlZGljdG9yIHZhcmlhYmxlIGluIHRoZSBmaW5hbCBSYW5kb20gRm9yZXN0IG1vZGVsIGlzIGFzc2Vzc2VkIHVzaW5nIHR3byBtZXRyaWNzOiBNZWFuIERlY3JlYXNlIGluIEFjY3VyYWN5IGFuZCBNZWFuIERlY3JlYXNlIGluIEdpbmkuIFRoZXNlIG1lYXN1cmVzIGFyZSBleHRyYWN0ZWQgYW5kIHZpc3VhbGl6ZWQgdG8gaGlnaGxpZ2h0IHRoZSBtb3N0IGluZmx1ZW50aWFsIHZhcmlhYmxlcywgcHJvdmlkaW5nIGluc2lnaHRzIGludG8gd2hpY2ggZmVhdHVyZXMgY29udHJpYnV0ZSB0aGUgbW9zdCB0byB0aGUgbW9kZWzigJlzIHByZWRpY3RpdmUgcG93ZXIuIFRoZSByZXN1bHRzIGFyZSBkaXNwbGF5ZWQgaW4gYSBiYXIgcGxvdCB0aGF0IHNlcGFyYXRlcyB0aGUgaW1wb3J0YW5jZSBzY29yZXMgZm9yIGVhY2ggbWV0cmljLgoKYGBge3J9CiMgRXh0cmFjdCBpbXBvcnRhbmNlIG1lYXN1cmVzCmltcG9ydGFuY2VfdmFscyA8LSBpbXBvcnRhbmNlKGZpbmFsLnJmLmNscykKaW1wb3J0YW5jZV9kZiA8LSBkYXRhLmZyYW1lKAogIFZhcmlhYmxlID0gcm93bmFtZXMoaW1wb3J0YW5jZV92YWxzKSwKICBNZWFuRGVjcmVhc2VBY2N1cmFjeSA9IGltcG9ydGFuY2VfdmFsc1ssICJNZWFuRGVjcmVhc2VBY2N1cmFjeSJdLAogIE1lYW5EZWNyZWFzZUdpbmkgPSBpbXBvcnRhbmNlX3ZhbHNbLCAiTWVhbkRlY3JlYXNlR2luaSJdCikKCgojIFBpdm90IGZvciBwbG90dGluZwppbXBvcnRhbmNlX2xvbmcgPC0gcGl2b3RfbG9uZ2VyKAogIGltcG9ydGFuY2VfZGYsCiAgY29scyA9IGMoTWVhbkRlY3JlYXNlQWNjdXJhY3ksIE1lYW5EZWNyZWFzZUdpbmkpLAogIG5hbWVzX3RvID0gIk1ldHJpYyIsCiAgdmFsdWVzX3RvID0gIkltcG9ydGFuY2UiCikKCiMgUGxvdApnZ3Bsb3QoaW1wb3J0YW5jZV9sb25nLCBhZXMoeCA9IHJlb3JkZXIoVmFyaWFibGUsIEltcG9ydGFuY2UpLCB5ID0gSW1wb3J0YW5jZSwgZmlsbCA9IE1ldHJpYykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgY29vcmRfZmxpcCgpICsKICBmYWNldF93cmFwKH4gTWV0cmljLCBzY2FsZXMgPSAiZnJlZV94IikgKwogIGxhYnModGl0bGUgPSAiVmFyaWFibGUgSW1wb3J0YW5jZSAoUmFuZG9tIEZvcmVzdCkiLAogICAgICAgeCA9ICJWYXJpYWJsZSIsCiAgICAgICB5ID0gIkltcG9ydGFuY2UgU2NvcmUiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgoKYGBgCgpGaWd1cmUgVi4gUmFuZG9tIEZvcmVzdCBGZWF0dXJlIEltcG9ydGFuY2UgVmlzdWFsaXphdGlvbgoKCiMjIENvbXBhcmlzb24gCgpUbyBldmFsdWF0ZSBhbmQgY29tcGFyZSB0aGUgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBvZiB0aGUgcHJ1bmVkIENBUlQgbW9kZWxzLCBsb2dpc3RpYyByZWdyZXNzaW9uLCBCYWdnaW5nLCBhbmQgUmFuZG9tIEZvcmVzdCwgd2UgZ2VuZXJhdGVkIFJPQyAoUmVjZWl2ZXIgT3BlcmF0aW5nIENoYXJhY3RlcmlzdGljKSBjdXJ2ZXMgdXNpbmcgdGhlIHRlc3QgZGF0YXNldC4gVGhlc2UgY3VydmVzIGlsbHVzdHJhdGUgdGhlIHRyYWRlLW9mZiBiZXR3ZWVuIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eSBhY3Jvc3MgYWxsIHBvc3NpYmxlIGNsYXNzaWZpY2F0aW9uIHRocmVzaG9sZHMsIHByb3ZpZGluZyBhIHZpc3VhbCBjb21wYXJpc29uIG9mIG1vZGVsIHBlcmZvcm1hbmNlIGFjcm9zcyBkaWZmZXJlbnQgbWV0aG9kcy4KCgoKYGBge3J9CgoKIyBGaXQgdGhlIExvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwKbG9naXRfbW9kZWwgPC0gZ2xtKFRlblllYXJDSEQgfiAuLCBkYXRhID0gdHJhaW4uZGF0YSwgZmFtaWx5ID0gYmlub21pYWwpCgojIFByZWRpY3QgcHJvYmFiaWxpdGllcyBmb3IgdGhlIHRlc3Qgc2V0CnByZWQubG9naXQucHJvYiA8LSBwcmVkaWN0KGxvZ2l0X21vZGVsLCBuZXdkYXRhID0gdGVzdC5kYXRhLCB0eXBlID0gInJlc3BvbnNlIikKCiMgRW5zdXJlIHJlc3BvbnNlIGlzIGEgZmFjdG9yIHdpdGggY29ycmVjdCBsZXZlbHMKdGVzdC5yZXNwb25zZSA8LSBmYWN0b3IodGVzdC5kYXRhJFRlblllYXJDSEQsIGxldmVscyA9IGMoMCwgMSkpCgojIENvbXB1dGUgUk9DIGFuZCBBVUMgZm9yIExvZ2lzdGljIFJlZ3Jlc3Npb24KbG9naXQucm9jIDwtIHJvYyhyZXNwb25zZSA9IHRlc3QucmVzcG9uc2UsIHByZWRpY3RvciA9IHByZWQubG9naXQucHJvYiwgbGV2ZWxzID0gYygiMCIsICIxIiksIGRpcmVjdGlvbiA9ICI8IikKbG9naXQuYXVjIDwtIHBST0M6OmF1Yyhsb2dpdC5yb2MpCgoKIyBSZWNvbXB1dGUgcHJlZGljdGlvbiwgZW5zdXJlIGl0J3MgYWxpZ25lZCBhbmQgdW4tbmFtZWQKcHJlZC5wcm9iLjFTRSA8LSBwcmVkaWN0KHBydW5lZC50cmVlLjFTRSwgbmV3ZGF0YSA9IHRlc3QuZGF0YSwgdHlwZSA9ICJwcm9iIilbLCAyXQpwcmVkLnByb2IuMVNFIDwtIGFzLm51bWVyaWMocHJlZC5wcm9iLjFTRSkgICMgcmVtb3ZlcyBhbnkgcm93IG5hbWVzCgojIFJPQyBmb3IgZWFjaCBtb2RlbCAoZW5zdXJlIHRoZSBtb2RlbCBwcm9iYWJpbGl0aWVzIGFyZSBkZWZpbmVkIGZpcnN0KQpyb2MudHJlZS4xU0UgPC0gcm9jKHRlc3QuZGF0YSRUZW5ZZWFyQ0hELCBwcmVkLnByb2IuMVNFKQoKcHJlZC5wcm9iLm1pbiA8LSBwcmVkaWN0KHBydW5lZC50cmVlLm1pbiwgbmV3ZGF0YSA9IHRlc3QuZGF0YSwgdHlwZSA9ICJwcm9iIilbLCAyXQpwcmVkLnByb2IubWluIDwtIGFzLm51bWVyaWMocHJlZC5wcm9iLm1pbikKCnJvYy50cmVlLm1pbiA8LSByb2ModGVzdC5kYXRhJFRlblllYXJDSEQsIHByZWQucHJvYi5taW4pCnJvYy5iYWdnaW5nIDwtIHJvYyh0ZXN0LmRhdGEkVGVuWWVhckNIRCwgcHJlZC5iYWcucHJvYikKcm9jLnJmIDwtIHJvYyh0ZXN0LmRhdGEkVGVuWWVhckNIRCwgcHJlZC5yZi5wcm9iKQpyb2MubG9naXQgPC0gcm9jKHRlc3QuZGF0YSRUZW5ZZWFyQ0hELCBwcmVkLmxvZ2l0LnByb2IpCgojIEFVQyB2YWx1ZXMKYXVjLnRyZWUuMVNFIDwtIHBST0M6OmF1Yyhyb2MudHJlZS4xU0UpCmF1Yy50cmVlLm1pbiA8LSBwUk9DOjphdWMocm9jLnRyZWUubWluKQphdWMuYmFnZ2luZyA8LSBwUk9DOjphdWMocm9jLmJhZ2dpbmcpCmF1Yy5yZiA8LSBwUk9DOjphdWMocm9jLnJmKQphdWMubG9naXQgPC0gcFJPQzo6YXVjKHJvYy5sb2dpdCkKCiMgUGxvdCBST0MgY3VydmVzIGZvciBjb21wYXJpc29uCnBhcihtYXIgPSBjKDUsIDUsIDQsIDEpKQpwbG90KDEgLSByb2MubG9naXQkc3BlY2lmaWNpdGllcywgcm9jLmxvZ2l0JHNlbnNpdGl2aXRpZXMsIAogICAgIHR5cGUgPSAibCIsIGNvbCA9ICJkYXJrb3JhbmdlIiwgbHdkID0gMiwgCiAgICAgeGxhYiA9ICIxIC0gU3BlY2lmaWNpdHkiLCB5bGFiID0gIlNlbnNpdGl2aXR5IiwgCiAgICAgbWFpbiA9ICJST0MgQ3VydmUgQ29tcGFyaXNvbiIpCgpsaW5lcygxIC0gcm9jLnRyZWUuMVNFJHNwZWNpZmljaXRpZXMsIHJvYy50cmVlLjFTRSRzZW5zaXRpdml0aWVzLCBjb2wgPSAiZ29sZCIsIGx3ZCA9IDIpCmxpbmVzKDEgLSByb2MudHJlZS5taW4kc3BlY2lmaWNpdGllcywgcm9jLnRyZWUubWluJHNlbnNpdGl2aXRpZXMsIGNvbCA9ICJwdXJwbGUiLCBsd2QgPSAyKQpsaW5lcygxIC0gcm9jLmJhZ2dpbmckc3BlY2lmaWNpdGllcywgcm9jLmJhZ2dpbmckc2Vuc2l0aXZpdGllcywgY29sID0gImJsdWUiLCBsd2QgPSAyKQpsaW5lcygxIC0gcm9jLnJmJHNwZWNpZmljaXRpZXMsIHJvYy5yZiRzZW5zaXRpdml0aWVzLCBjb2wgPSAiZm9yZXN0Z3JlZW4iLCBsd2QgPSAyKQoKIyBBZGQgYSBsZWdlbmQgZm9yIGNsYXJpdHkKbGVnZW5kKCJib3R0b21yaWdodCIsCiAgICAgICBsZWdlbmQgPSBjKCJMb2dpc3RpYyIsICJDQVJUIDFTRSIsICJDQVJUIE1pbiIsICJSYW5kb20gRm9yZXN0IiwgIkJhZ2dpbmciKSwKICAgICAgIGNvbCA9IGMoImRhcmtvcmFuZ2UiLCAiZ29sZCIsICJwdXJwbGUiLCAiYmx1ZSIsICJmb3Jlc3RncmVlbiIpLAogICAgICAgbHR5ID0gYygxLCAxLCAxLCAxLCAxKSwKICAgICAgIGx3ZCA9IDIsCiAgICAgICBidHkgPSAibiIsCiAgICAgICBjZXggPSAwLjYpCgojIyMgPT09IEFubm90YXRlIHdpdGggQVVDcyA9PT0gIyMjCiMgQW5ub3RhdGluZyB0aGUgcGxvdCB3aXRoIHRoZSBBVUMgdmFsdWVzCnRleHQoMC43NSwgMC40OCwgcGFzdGUoIkxvZ2lzdGljIEFVQzogIiwgcm91bmQoYXVjLmxvZ2l0LCA0KSksIGNleCA9IDAuNykKdGV4dCgwLjc1LCAwLjQyLCBwYXN0ZSgiQ0FSVCAxU0UgQVVDOiAiLCByb3VuZChhdWMudHJlZS4xU0UsIDQpKSwgY2V4ID0gMC43KQp0ZXh0KDAuNzUsIDAuMzYsIHBhc3RlKCJDQVJUIE1pbiBBVUM6ICIsIHJvdW5kKGF1Yy50cmVlLm1pbiwgNCkpLCBjZXggPSAwLjcpCnRleHQoMC43NSwgMC4zMCwgcGFzdGUoIlJhbmRvbSBGb3Jlc3QgQVVDOiAiLCByb3VuZChhdWMucmYsIDQpKSwgY2V4ID0gMC43KQp0ZXh0KDAuNzUsIDAuMjQsIHBhc3RlKCJCYWdnaW5nIEFVQzogIiwgcm91bmQoYXVjLmJhZ2dpbmcsIDQpKSwgY2V4ID0gMC43KQoKYGBgCgoKCkZpZ3VyZSBXLiBST0MgQ3VydmVzIGZvciBDbGFzc2lmaWNhdGlvbiBNb2RlbHMuIAoKUmVjZWl2ZXIgT3BlcmF0aW5nIENoYXJhY3RlcmlzdGljIChST0MpIEN1cnZlcyBmb3IgTXVsdGlwbGUgUHJlZGljdGl2ZSBNb2RlbHM6IFRoZSBwbG90IGNvbXBhcmVzIHRoZSBST0MgY3VydmVzIGZvciBMb2dpc3RpYyBSZWdyZXNzaW9uLCBDQVJUIHdpdGggMS1TRSBwcnVuaW5nLCBDQVJUIHdpdGggTWluaW11bSBDb3N0LUNvbXBsZXhpdHkgUHJ1bmluZywgQmFnZ2luZywgYW5kIFJhbmRvbSBGb3Jlc3QuIEFVQyB2YWx1ZXMgZm9yIGVhY2ggbW9kZWwgYXJlIGFubm90YXRlZCwgaGlnaGxpZ2h0aW5nIHRoZWlyIGNsYXNzaWZpY2F0aW9uIHBlcmZvcm1hbmNlLiBBbW9uZyB0aGUgbW9kZWxzIGV2YWx1YXRlZCwgTG9naXN0aWMgUmVncmVzc2lvbiBhY2hpZXZlZCB0aGUgaGlnaGVzdCBBVUMsIGRlbW9uc3RyYXRpbmcgc3VwZXJpb3IgYWNjdXJhY3kgaW4gZGlzdGluZ3Vpc2hpbmcgYmV0d2VlbiBpbmRpdmlkdWFscyB3aG8gd2lsbCBhbmQgd2lsbCBub3QgZGV2ZWxvcCBjb3JvbmFyeSBoZWFydCBkaXNlYXNlIG92ZXIgYSB0ZW4teWVhciBwZXJpb2QuIFRoaXMgc3VnZ2VzdHMgdGhhdCwgaW4gdGhpcyBzY2VuYXJpbywgTG9naXN0aWMgUmVncmVzc2lvbiBwcm92aWRlcyBhIG1vcmUgcmVsaWFibGUgcmlzayBwcmVkaWN0aW9uIHRvb2wgY29tcGFyZWQgdG8gdGhlIG90aGVyIG1vZGVscy4KCiMjIE9wdGltYWwgQ3V0b2ZmCgpUaGUgb3B0aW1hbCBjdXRvZmYgcmVwcmVzZW50cyB0aGUgdGhyZXNob2xkIHByb2JhYmlsaXR5IGF0IHdoaWNoIHRoZSBjbGFzc2lmaWNhdGlvbiBtb2RlbCBiYWxhbmNlcyBzZW5zaXRpdml0eSBhbmQgc3BlY2lmaWNpdHksIG1heGltaXppbmcgdGhlIG92ZXJhbGwgcGVyZm9ybWFuY2Ugb2YgdGhlIG1vZGVsLiBCeSB1c2luZyB0aGlzIGN1dG9mZiwgd2UgZW5zdXJlIHRoYXQgcHJlZGljdGlvbnMgYXJlIG1hZGUgd2l0aCB0aGUgbW9zdCBlZmZlY3RpdmUgdHJhZGUtb2ZmIGJldHdlZW4gY29ycmVjdGx5IGlkZW50aWZ5aW5nIHBvc2l0aXZlIGNhc2VzIGFuZCBtaW5pbWl6aW5nIGZhbHNlIHBvc2l0aXZlcywgd2hpY2ggaXMgY3J1Y2lhbCBmb3IgbWFraW5nIHJlbGlhYmxlIGRlY2lzaW9ucyBpbiBwcmFjdGljYWwgYXBwbGljYXRpb25zIHN1Y2ggYXMgaGVhbHRoY2FyZSBvciBmaW5hbmNlLgoKCmBgYHtyfQojIEZpbmQgdGhlIG9wdGltYWwgY3V0b2ZmIHVzaW5nIFlvdWRlbidzIEogc3RhdGlzdGljCmNvb3Jkcy5vcHRpbWFsIDwtIGNvb3Jkcyhyb2MubG9naXQsIHggPSAiYmVzdCIsIGJlc3QubWV0aG9kID0gInlvdWRlbiIsIHJldCA9IGMoInRocmVzaG9sZCIsICJzZW5zaXRpdml0eSIsICJzcGVjaWZpY2l0eSIpKQoKIyBQcmludCB0aGUgb3B0aW1hbCBjdXRvZmYgYW5kIGFzc29jaWF0ZWQgc2Vuc2l0aXZpdHkvc3BlY2lmaWNpdHkKcHJpbnQoY29vcmRzLm9wdGltYWwpCgpgYGAKCgpUYWJsZSAxMi4gVGhlIG9wdGltYWwgY3V0b2ZmLiAKCgpUaGUgdGhyZXNob2xkIHZhbHVlIG9mIDAuMTU2MzcxOSBpcyB0aGUgb3B0aW1hbCBjdXRvZmYuIEl0IGlzIGNob3NlbiB0byBiYWxhbmNlIHRoZSBzZW5zaXRpdml0eSBhbmQgc3BlY2lmaWNpdHkuIElmIHlvdSBhcmUgdXNpbmcgdGhpcyB0aHJlc2hvbGQsIHRoZSBtb2RlbCB3aWxsIGNsYXNzaWZ5IGEgcGVyc29uIGFzIGF0IHJpc2sgZm9yIGNvcm9uYXJ5IGhlYXJ0IGRpc2Vhc2UgaWYgdGhlaXIgcHJlZGljdGVkIHByb2JhYmlsaXR5IGlzIGdyZWF0ZXIgdGhhbiAxNS42JS4gQSBzZW5zaXRpdml0eSBvZiA2My4yJSBpbmRpY2F0ZXMgdGhhdCB0aGUgbW9kZWwgaXMgZmFpcmx5IGdvb2QgYXQgaWRlbnRpZnlpbmcgaW5kaXZpZHVhbHMgd2hvIHdpbGwgZGV2ZWxvcCBjb3JvbmFyeSBoZWFydCBkaXNlYXNlLiBIb3dldmVyLCBpdCBtaXNzZXMgYWJvdXQgMzYuOCUgb2YgdGhlIHRydWUgcG9zaXRpdmVzIChmYWxzZSBuZWdhdGl2ZXMpLiBPbiB0aGUgb3RoZXIgaGFuZCwgdGhlIHNwZWNpZmljaXR5IG9mIDcwLjMlIGluZGljYXRlcyB0aGF0IHRoZSBtb2RlbCBkb2VzIGEgYmV0dGVyIGpvYiBvZiBjb3JyZWN0bHkgaWRlbnRpZnlpbmcgaW5kaXZpZHVhbHMgd2hvIHdpbGwgbm90IGRldmVsb3AgdGhlIGRpc2Vhc2UsIHRob3VnaCB0aGVyZSBpcyBzdGlsbCBzb21lIHJvb20gZm9yIGltcHJvdmVtZW50IChhYm91dCAyOS43JSBmYWxzZSBwb3NpdGl2ZXMpLgoKCiMgQ09OQ0xVU0lPTgoKVGhlIHJlZ3Jlc3Npb24gbW9kZWxzIGV2YWx1YXRlZCwgaW5jbHVkaW5nIFBydW5lZCBUcmVlcyAoYm90aCAxLVNFIGFuZCBNaW5pbXVtIENvc3QtQ29tcGxleGl0eSksIEJhZ2dpbmcsIGFuZCBSYW5kb20gRm9yZXN0LCBwcm92aWRlZCB2YWx1YWJsZSBpbnNpZ2h0cyBpbnRvIHByZWRpY3RpbmcgdG90YWwgY2hvbGVzdGVyb2wgbGV2ZWxzICh0b3RDaG9sKS4gQW1vbmcgdGhlc2UsIHRoZSBSYW5kb20gRm9yZXN0IG1vZGVsIGRlbW9uc3RyYXRlZCB0aGUgYmVzdCBwZXJmb3JtYW5jZSB3aXRoIHRoZSBsb3dlc3QgUk1TRSBhbmQgaGlnaGVzdCBSLXNxdWFyZWQgdmFsdWUuIFRoZSBQcnVuZWQgVHJlZSAoTWluaW11bSBDb3N0LUNvbXBsZXhpdHkpIG1vZGVsLCB3aGlsZSBzdGlsbCBwZXJmb3JtaW5nIHdlbGwsIHNob3dlZCBhIGhpZ2hlciBSTVNFLCBzdWdnZXN0aW5nIHRoYXQgaXRzIHByZWRpY3Rpb25zIHdlcmUgbGVzcyBhY2N1cmF0ZS4gR2l2ZW4gdGhlIG5hdHVyZSBvZiB0aGUgdGFza+KAlHByZWRpY3RpbmcgYSBjb250aW51b3VzIHZhcmlhYmxlIHN1Y2ggYXMgY2hvbGVzdGVyb2wgbGV2ZWxz4oCUaXQgaXMgcmVjb21tZW5kZWQgdG8gdXRpbGl6ZSBSYW5kb20gRm9yZXN0IGFzIHRoZSBwcmltYXJ5IG1vZGVsIGZvciBmdXR1cmUgcHJlZGljdGlvbnMgZHVlIHRvIGl0cyBoaWdoZXIgYWNjdXJhY3kgYW5kIHJvYnVzdG5lc3MuIEhvd2V2ZXIsIGZvciBpbnRlcnByZXRhYmlsaXR5IGFuZCBzaW1wbGljaXR5LCBQcnVuZWQgVHJlZXMgbWF5IGFsc28gYmUgY29uc2lkZXJlZCB3aGVuIGEgbW9yZSB0cmFuc3BhcmVudCBtb2RlbCBpcyBuZWVkZWQuCgoKRm9yIGNsYXNzaWZpY2F0aW9uLCB0aGUgY29tcGFyaXNvbiBvZiBtb2RlbHMgc3VjaCBhcyBMb2dpc3RpYyBSZWdyZXNzaW9uLCBDQVJUICgxLVNFIGFuZCBNaW4pLCBCYWdnaW5nLCBhbmQgUmFuZG9tIEZvcmVzdCBmb3IgcHJlZGljdGluZyBjb3JvbmFyeSBoZWFydCBkaXNlYXNlIChUZW5ZZWFyQ0hEKSByZXZlYWxlZCB0aGF0IExvZ2lzdGljIFJlZ3Jlc3Npb24gb3V0cGVyZm9ybWVkIGFsbCBvdGhlciBtb2RlbHMsIGFjaGlldmluZyB0aGUgaGlnaGVzdCBBVUMuIFRoaXMgc3VnZ2VzdHMgdGhhdCBMb2dpc3RpYyBSZWdyZXNzaW9uIGlzIHBhcnRpY3VsYXJseSBlZmZlY3RpdmUgYXQgZGlzdGluZ3Vpc2hpbmcgYmV0d2VlbiBpbmRpdmlkdWFscyB3aG8gd2lsbCBhbmQgd2lsbCBub3QgZGV2ZWxvcCBjb3JvbmFyeSBoZWFydCBkaXNlYXNlIGluIHRoZSBuZXh0IHRlbiB5ZWFycy4gV2hpbGUgUmFuZG9tIEZvcmVzdCBhbmQgQmFnZ2luZyBzaG93ZWQgc3Ryb25nIHBlcmZvcm1hbmNlLCBMb2dpc3RpYyBSZWdyZXNzaW9uIHByb3ZpZGVkIGEgbW9yZSByZWxpYWJsZSBhbmQgc2ltcGxlciBtb2RlbCBmb3IgdGhpcyB0YXNrLiBUaGVyZWZvcmUsIGl0IGlzIHJlY29tbWVuZGVkIHRvIHByaW9yaXRpemUgTG9naXN0aWMgUmVncmVzc2lvbiBmb3IgZnV0dXJlIHVzZSBpbiBjbGluaWNhbCBhcHBsaWNhdGlvbnMgd2hlcmUgdW5kZXJzdGFuZGluZyBhbmQgZXhwbGFpbmluZyB0aGUgcmlzayBvZiBjb3JvbmFyeSBoZWFydCBkaXNlYXNlIGlzIGNyaXRpY2FsLiBGb3IgaW1wcm92ZWQgcGVyZm9ybWFuY2UgaW4gbW9yZSBjb21wbGV4IGRhdGFzZXRzIG9yIG5vbi1saW5lYXIgcmVsYXRpb25zaGlwcywgUmFuZG9tIEZvcmVzdCBhbmQgQmFnZ2luZyBjYW4gYmUgY29uc2lkZXJlZCBhcyBhbHRlcm5hdGl2ZSBtb2RlbHMgZm9yIGZ1cnRoZXIgZXhwbG9yYXRpb24uCgo=